commit - /dev/null
commit + 06f09e349815cf018a2919a3f152cee136bb763c
blob - /dev/null
blob + ef03406c437f8341575b389e203d5fe70e999ea3 (mode 644)
--- /dev/null
+++ lib/Bot/BasicBot/Pluggable/Module/Wiki.pm
+package Bot::BasicBot::Pluggable::Module::Wiki;
+
+use warnings;
+use strict;
+use XML::RSS::Parser;
+use HTTP::Tiny;
+use Time::Piece; # convert time string to epoch
+use Digest::MD5 qw(md5_hex); # Store rss url as md5_hex
+use base qw(Bot::BasicBot::Pluggable::Module);
+
+our $VERSION = '0.02';
+
+
+sub help {
+ return "wiki [list]";
+}
+
+sub init {
+ my $self = shift;
+ $self->{tick} = 0;
+ $self->{delay} = 60;
+ $self->{limit} = 5;
+ $self->{notify} = {
+ links=>{},
+ feeds=>{},
+ };
+ $self->config(
+ {
+ feeds => {},
+ user_delay => 60, # 5 min delay between checks
+# user_init_headlines_seen => 1,
+# user_debug => 0,
+# user_tmpdir => File::Spec->tmpdir(),
+# user_format => 'RSS: %h <%u>'
+ }
+ );
+ # $self->{feeds} = $self->get('feeds');
+}
+# Set feeds
+sub feeds {
+ my $self=shift;
+ my $feeds=shift;
+ for my $feed (keys %{$feeds}) {
+ $self->{feeds}->{$feed}={
+ feed_id=>md5_hex($feed),
+ channels=>$feeds->{$feed},
+ }
+ }
+}
+
+
+sub list_feeds {
+ my ($self,$channel) = @_;
+
+ my $reply;
+ for my $url (keys %{$self->{feeds}}) {
+ $reply .= "$url\n" ;
+ my $chan_str="";
+ for my $chan (@{$self->{feeds}->{$url}->{channels}}) {
+ $chan_str .= "$chan,";
+ }
+ chop($chan_str); #remove last coma
+ $reply .= "$chan_str\n";
+ }
+
+ $self->fetch_rss();
+ if ($reply) {
+ return $reply;
+ } else {
+ return 'No feeds to list';
+ }
+}
+
+sub parse_feed {
+ my $self = shift;
+ my $feed = shift;
+ my $feed_id = shift;
+ my $uriHash = shift;
+ my $rss = HTTP::Tiny->new->get($feed);
+ my $p = XML::RSS::Parser->new;
+ my $data = $p->parse_string($rss->{content});
+
+ if (not defined $data) {
+ print $p->errstr;
+ return 0; #failed to parse to return fail.
+ }
+ my $notify=$self->{notify};
+ my $dateFormat = qq|%a, %d %b %Y %T %Z|;
+ foreach my $i ( $data->query('//item') ) {
+ my $date = $i->query('pubDate');
+ my $tp = Time::Piece->strptime($date->text_content, $dateFormat);
+ my $epoch=$tp->epoch;
+ my $title = $i->query('title');
+ my $contributor = $i->query('dc:contributor');
+ my $link = $i->query('link');
+ my $link_id = md5_hex($link->text_content);
+ if (not exists $uriHash->{$link_id} or ($epoch > $uriHash->{$link_id})) {
+ $uriHash->{$link_id} = $epoch;
+ $notify->{links}->{$link_id}->{message} = $contributor->text_content." updated ".$title->text_content." on ".$date->text_content
+ ."\n ".$link->text_content."\n";
+ push (@{$notify->{feeds}->{$feed_id}},$link_id);
+ }
+ }
+}
+
+
+# $self->{notify}->{
+# links=>{
+# $link_id=>{
+# message=>
+# channels=>{}
+# }
+# }
+# feeds=>{
+# $feed_id => [$link_ids]
+# }
+#
+
+
+sub tick {
+ my $self=shift;
+ $self->{tick} += 1;
+ my $notify=$self->{notify};
+
+ # See if there are any nessages to send out.
+ # Limit notifications to $self->{limit}
+ my $count=0;
+ for my $link_id (keys %{$notify->{links}}) {
+ my $msg=$notify->{links}->{$link_id}->{message};
+ next if not length($msg);
+ for my $chan (keys %{$notify->{links}->{$link_id}->{channels}}) {
+ $self->tell( $chan, $msg);
+ $count++;
+ }
+ delete $notify->{links}->{$link_id};
+ return if ($count > $self->{limit});
+ }
+
+ # Increase the tick counter and return unless its time to check for updats
+ return if ($self->{tick} < $self->{delay});
+ $self->{tick} = 0;
+
+ $self->fetch_rss();
+}
+
+sub fetch_rss {
+ my $self = shift;
+ my $notify=$self->{notify};
+
+ # Check for updates to the feed
+ my $uriHash = $self->get('uriHash') || {};
+ for my $feed (keys %{$self->{feeds}}) {
+ my $feed_id=$self->{feeds}->{$feed}->{feed_id};
+ # parse the feed
+ $self->parse_feed($feed,$feed_id,$uriHash);
+ # find all the link_ids parsed for this feed
+ for my $link_id (@{$notify->{feeds}->{$feed_id}}) {
+ # Add the list of channels for each feed to the notify channel list for each link_id
+ for my $chan (@{$self->{feeds}->{$feed}->{channels}}) {
+ $notify->{links}->{$link_id}->{channels}->{$chan}=1;
+ }
+ }
+ }
+
+ $self->set('uriHash', $uriHash);
+}
+
+
+sub told {
+ my ($self,$message) = @_;
+ # Only act if we are addressed
+ if ($message->{address}) {
+ my $body = $message->{body};
+ my $channel = $message->{channel};
+
+ if ($channel eq 'msg') {
+ $channel = $message->{who};
+ }
+
+ my @cmds = split(' ',$body);
+ if ($cmds[0] eq '!wiki' or ($channel eq $message->{who} and $cmds[0] eq 'wiki')) {
+ my %actions = (
+ # add => sub { return $self->add_feed( $channel, @_ ) },
+ list => sub { return $self->list_feeds($channel); },
+ # remove => sub { return $self->remove_feed( $channel, @_ ) },
+ );
+ if (!defined($actions{$cmds[1]})) {
+ return $self->help();
+ }
+ my $reply = $actions{$cmds[1]}->(@cmds[2,-1]) ;
+ return $reply;
+ }
+ }
+}
+
+
+
+
+1; # End of Bot::BasicBot::Pluggable::Module::Wiki
+
+__END__
+
+=head1 NAME
+
+Bot::BasicBot::Pluggable::Module::Wiki - Update parser for pmWiki RSS feed
+
+=head1 VERSION
+
+Version 0.02
+
+=head1 SYNOPSIS
+
+ wiki list
+
+=head1 DESCRIPTION
+
+This module enables your bot to monitor various RSS feeds for new
+posts or updates to pmWiki pages. Currently configuration is done
+through variables you pass to the module from your bot. Future versions
+will have the ability to add RSS feeds to monitor on a per channel bases.
+
+=head1 VARIABLES
+
+=head2 feeds
+
+A hash reference of rss feeds urls to monitor for changes containing an array of irc channels to publish updates to.
+Currently passed in from your bot's main loop like this:
+
+$bot->{wiki}->feeds = {
+ 'https://wiki.ircnow.org/pmwiki.php?n=Site.AllRecentChanges&action=rss' => ['#planetofnix','#planetofnix-team'],
+}
+
+Future versions will have bot commands to add and modify the list.
+
+=head1 LIMITATIONS
+
+ This is still a work in progress.
+
+=head1 TODO
+
+ This is still a work in progress. It's a working proof of concept with room for improvement.
+
+=head1 AUTHOR
+
+Izzy Blacklock, C<< <izzyb at planetofnix.com> >>
+
+=head1 BUGS
+
+Please report any bugs or feature requests to C<izzyb at planetofnix.com>.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc Bot::BasicBot::Pluggable::Module::Wiki
+
+
+=head1 SEE ALSO
+
+L<Bot::BasicBot::Pluggable>
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2023 Izzy Blacklock, all rights reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+