commit 06f09e349815cf018a2919a3f152cee136bb763c from: Izzy Blacklock date: Sat Apr 29 00:33:28 2023 UTC Initial commit of Bot::BasicBot::Pluggable::Module::Wiki. Confirmed working but needs more polish. can pull rss feed for pmWiki and post updates to specified channels. commit - /dev/null commit + 06f09e349815cf018a2919a3f152cee136bb763c blob - /dev/null blob + ef03406c437f8341575b389e203d5fe70e999ea3 (mode 644) --- /dev/null +++ lib/Bot/BasicBot/Pluggable/Module/Wiki.pm @@ -0,0 +1,268 @@ +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<< >> + +=head1 BUGS + +Please report any bugs or feature requests to C. + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command. + + perldoc Bot::BasicBot::Pluggable::Module::Wiki + + +=head1 SEE ALSO + +L + +=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. +