File Coverage

blib/lib/Bot/IRC/X/Feeds.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Bot::IRC::X::Feeds;
2             # ABSTRACT: Bot::IRC plugin to watch and notify on changes in RSS feeds
3              
4 1     1   153760 use strict;
  1         2  
  1         29  
5 1     1   3 use warnings;
  1         2  
  1         21  
6              
7 1     1   675 use XML::RSS;
  0            
  0            
8             use LWP::UserAgent;
9             use LWP::Protocol::https;
10             use Date::Parse 'str2time';
11             use WWW::Shorten qw( TinyURL makeashorterlink );
12              
13             our $VERSION = '1.01'; # VERSION
14              
15             sub init {
16             my ($bot) = @_;
17             $bot->load('Store');
18             $bot->load('Join');
19              
20             $bot->hook(
21             {
22             to_me => 1,
23             text => qr/
24             ^feed[s]?
25             \s+(?<action>add|list|remove)
26             (?:
27             \s+(?<url>\S+)
28             (?:
29             \s+(?<forums>\S+)
30             )?
31             )?
32             /x,
33             },
34             sub {
35             my ( $bot, $in, $m ) = @_;
36             my @urls = @{ $bot->store->get('urls') || [] };
37              
38             if ( $m->{action} eq 'add' ) {
39             my $url = lc( $m->{url} );
40             if ( grep { $_->{url} eq $url } @urls ) {
41             $bot->reply_to(q{I'm already tracking that feed.});
42             }
43             else {
44             push( @urls, {
45             url => $url,
46             forums => [ split( ',', lc $m->{forums} ) ],
47             } );
48             $bot->reply_to(q{OK. I'll report changes from the feed you provided.});
49             $bot->store->set( 'urls' => \@urls );
50             }
51             }
52             elsif ( $m->{action} eq 'list' ) {
53             if (@urls) {
54             $bot->reply($_) for ( map {
55             ( @{ $_->{forums} } )
56             ? $_->{url} . ' (' . join( ', ', @{ $_->{forums} } ) . ')'
57             : $_->{url}
58             } @urls );
59             }
60             else {
61             $bot->reply_to(q{I'm currently not tracking any feeds.});
62             }
63             }
64             elsif ( $m->{action} eq 'remove' ) {
65             my $url = lc( $m->{url} );
66             if ( grep { $_->{url} eq $url } @urls ) {
67             $bot->reply_to(q{OK. I'll stop reporting on the feed you provided.});
68             $bot->store->set( 'urls' => [ grep { $_->{url} ne $url } @urls ] );
69             }
70             else {
71             $bot->reply_to(qq{I wasn't able to find the feed you specified. ($url)});
72             }
73             }
74              
75             return 1;
76             },
77             );
78              
79             my $ua = LWP::UserAgent->new;
80             my $rss = XML::RSS->new;
81             my $interval = ( $bot->vars->{interval} || 10 ) * 60;
82             my $max_per = $bot->vars->{max_per} || 5;
83             my $fresh_intervals = $bot->vars->{fresh_intervals} || 2;
84              
85             $bot->tick(
86             $interval,
87             sub {
88             my ($bot) = @_;
89              
90             my $seen = $bot->store->get('seen') || {};
91             my $now = time;
92             my $since = $now - $interval * $fresh_intervals;
93             my $channels = $bot->channels;
94              
95             for ( keys %$seen ) {
96             delete $seen->{$_} if ( $seen->{$_} < $since );
97             }
98              
99             for my $url ( @{ $bot->store->get('urls') || [] } ) {
100             my $res = $ua->get( $url->{url} );
101             next unless ( $res->is_success );
102              
103             eval {
104             $rss->parse( $res->decoded_content );
105             };
106             if ($@) {
107             warn $@;
108             next;
109             }
110              
111             my $printed = 0;
112             for my $item ( @{ $rss->{items} } ) {
113             my $time = str2time( $item->{pubDate} );
114             next if ( $time < $since );
115              
116             my $key = join( '|',
117             $url->{url},
118             $item->{title},
119             $item->{link},
120             $time,
121             );
122             next if ( $seen->{$key} );
123             $seen->{$key} = $now;
124              
125             my $msg =
126             'Feed: ' . $rss->channel('title') .
127             ' [ ' . $item->{title} . ' ]' .
128             ' (' . makeashorterlink( $item->{link} ) . ')';
129             $msg .= ' -- ' . $item->{comments} if ( $item->{comments} );
130              
131             $bot->msg( $_, $msg ) for ( @{ $url->{forums} } || @$channels );
132              
133             last if ( ++$printed >= $max_per );
134             }
135             }
136              
137             $bot->store->set( 'seen' => $seen );
138             },
139             );
140              
141             $bot->helps( feeds =>
142             'Watch and notify on changes in RSS feeds. ' .
143             'Usage: bot feed add URL [FORUMS], bot feed list, bot feed remove URL. ' .
144             'See also: https://metacpan.org/pod/Bot::IRC::X::Feeds'
145             );
146             }
147              
148             1;
149              
150             __END__
151              
152             =pod
153              
154             =encoding UTF-8
155              
156             =head1 NAME
157              
158             Bot::IRC::X::Feeds - Bot::IRC plugin to watch and notify on changes in RSS feeds
159              
160             =head1 VERSION
161              
162             version 1.01
163              
164             =for markdown [![Build Status](https://travis-ci.org/gryphonshafer/Bot-IRC-X-Feeds.svg)](https://travis-ci.org/gryphonshafer/Bot-IRC-X-Feeds)
165             [![Coverage Status](https://coveralls.io/repos/gryphonshafer/Bot-IRC-X-Feeds/badge.png)](https://coveralls.io/r/gryphonshafer/Bot-IRC-X-Feeds)
166              
167             =head1 SYNOPSIS
168              
169             use Bot::IRC;
170              
171             Bot::IRC->new(
172             connect => { server => 'irc.perl.org' },
173             plugins => ['Feeds'],
174             vars => {
175             x-feeds => {
176             interval => 10,
177             max_per => 5,
178             fresh_intervals => 2,
179             }
180             },
181             )->run;
182              
183             =head1 DESCRIPTION
184              
185             This L<Bot::IRC> plugin adds functionality so bots can watch and notify on
186             changes in RSS feeds. You can tell the bot to start following feeds.
187              
188             bot feed add URL [FORUMS]
189              
190             You can optionally provide a "FORUMS" string, which is a list of channels the
191             bot should report on for the feed. By default, the bot reports on all channels
192             it has joined. (Requires the L<Bot::IRC::Join> plugin.) The list of channels
193             must be comma-delimited with no spaces.
194              
195             You can list the feeds the bot is following:
196              
197             bot feed list
198              
199             And you can remove feeds from being watched:
200              
201             bot feed remove URL
202              
203             =head2 Configuration Settings
204              
205             Setting the C<x-feeds> values allows for configuration.
206              
207             Bot::IRC->new(
208             connect => { server => 'irc.perl.org' },
209             plugins => ['Feeds'],
210             vars => {
211             x-feeds => {
212             interval => 10,
213             max_per => 5,
214             fresh_intervals => 2,
215             }
216             },
217             )->run;
218              
219             The "interval" value is the time interval between calls to feeds, measured in
220             minutes.
221              
222             The "max_per" value is the number of items returned per feed per call.
223              
224             The "fresh_intervals" setting means how many intervals of time backward should
225             items be considered fresh enough to report on. For example, if you set an
226             interval of 5 minutes and a fresh_intervals of 3, then any item in a feed with
227             a publication time older than 15 will not be reported.
228              
229             The default values for all are shown in the example above.
230              
231             =head1 SEE ALSO
232              
233             You can look for additional information at:
234              
235             =over 4
236              
237             =item *
238              
239             L<Bot::IRC>
240              
241             =item *
242              
243             L<GitHub|https://github.com/gryphonshafer/Bot-IRC-X-Feeds>
244              
245             =item *
246              
247             L<CPAN|http://search.cpan.org/dist/Bot-IRC-X-Feeds>
248              
249             =item *
250              
251             L<MetaCPAN|https://metacpan.org/pod/Bot::IRC::X::Feeds>
252              
253             =item *
254              
255             L<AnnoCPAN|http://annocpan.org/dist/Bot-IRC-X-Feeds>
256              
257             =item *
258              
259             L<Travis CI|https://travis-ci.org/gryphonshafer/Bot-IRC-X-Feeds>
260              
261             =item *
262              
263             L<Coveralls|https://coveralls.io/r/gryphonshafer/Bot-IRC-X-Feeds>
264              
265             =item *
266              
267             L<CPANTS|http://cpants.cpanauthors.org/dist/Bot-IRC-X-Feeds>
268              
269             =item *
270              
271             L<CPAN Testers|http://www.cpantesters.org/distro/T/Bot-IRC-X-Feeds.html>
272              
273             =back
274              
275             =for Pod::Coverage init
276              
277             =head1 AUTHOR
278              
279             Gryphon Shafer <gryphon@cpan.org>
280              
281             =head1 COPYRIGHT AND LICENSE
282              
283             This software is copyright (c) 2016 by Gryphon Shafer.
284              
285             This is free software; you can redistribute it and/or modify it under
286             the same terms as the Perl 5 programming language system itself.
287              
288             =cut