line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::RSS::FromHTML; |
2
|
12
|
|
|
12
|
|
459311
|
use base Class::Accessor::Fast; |
|
12
|
|
|
|
|
32
|
|
|
12
|
|
|
|
|
12877
|
|
3
|
12
|
|
|
12
|
|
82440
|
use strict; |
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
351
|
|
4
|
12
|
|
|
12
|
|
74
|
use Carp; |
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
1036
|
|
5
|
12
|
|
|
12
|
|
24757
|
use XML::RSS (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use LWP::UserAgent (); |
7
|
|
|
|
|
|
|
use HTTP::Cookies (); |
8
|
|
|
|
|
|
|
use Data::Dumper (); |
9
|
|
|
|
|
|
|
use bytes (); |
10
|
|
|
|
|
|
|
use File::Basename (); |
11
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw( |
14
|
|
|
|
|
|
|
name |
15
|
|
|
|
|
|
|
url |
16
|
|
|
|
|
|
|
cacheDir |
17
|
|
|
|
|
|
|
feedDir |
18
|
|
|
|
|
|
|
rssObj |
19
|
|
|
|
|
|
|
minInterval |
20
|
|
|
|
|
|
|
passthru |
21
|
|
|
|
|
|
|
debug |
22
|
|
|
|
|
|
|
unicodeDowngrade |
23
|
|
|
|
|
|
|
maxItemCount |
24
|
|
|
|
|
|
|
outFileName |
25
|
|
|
|
|
|
|
updateStatus |
26
|
|
|
|
|
|
|
newItems |
27
|
|
|
|
|
|
|
)); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub new { |
30
|
|
|
|
|
|
|
my $self = shift; |
31
|
|
|
|
|
|
|
my $p = bless({},$self); |
32
|
|
|
|
|
|
|
# set default values |
33
|
|
|
|
|
|
|
$p->name('myrss'); |
34
|
|
|
|
|
|
|
$p->cacheDir('.'); |
35
|
|
|
|
|
|
|
$p->feedDir('.'); |
36
|
|
|
|
|
|
|
$p->minInterval(300); # in seconds |
37
|
|
|
|
|
|
|
$p->maxItemCount(30); |
38
|
|
|
|
|
|
|
$p->passthru({}); |
39
|
|
|
|
|
|
|
$p->updateStatus('update not executed yet'); |
40
|
|
|
|
|
|
|
# initialize properties (for sub-classes) |
41
|
|
|
|
|
|
|
$p->init(@_); |
42
|
|
|
|
|
|
|
return $p; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub update { |
46
|
|
|
|
|
|
|
my $self = shift; |
47
|
|
|
|
|
|
|
### define output files for debug |
48
|
|
|
|
|
|
|
my $dbg = { |
49
|
|
|
|
|
|
|
interval => $self->cacheDir.'/'.$self->name.'.intv', |
50
|
|
|
|
|
|
|
html => $self->cacheDir.'/'.$self->name.'.html', |
51
|
|
|
|
|
|
|
list => $self->cacheDir.'/'.$self->name.'.list', |
52
|
|
|
|
|
|
|
update => $self->cacheDir.'/'.$self->name.'.update', |
53
|
|
|
|
|
|
|
newcount => $self->cacheDir.'/'.$self->name.'.new.count', |
54
|
|
|
|
|
|
|
}; |
55
|
|
|
|
|
|
|
if($self->debug){ |
56
|
|
|
|
|
|
|
unlink $dbg->{$_} foreach(keys %{$dbg}); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
### check minimum interval |
59
|
|
|
|
|
|
|
my ($getOk,$okTime,$nowTime) = $self->checkInterval(); |
60
|
|
|
|
|
|
|
unless($getOk){ |
61
|
|
|
|
|
|
|
# debug |
62
|
|
|
|
|
|
|
if($self->debug){ |
63
|
|
|
|
|
|
|
open(OUT,'>',$dbg->{interval}) or confess $!; |
64
|
|
|
|
|
|
|
print OUT "now : ${nowTime}\nok : ${okTime}"; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
$self->updateStatus("still under check interval time period"); |
67
|
|
|
|
|
|
|
return 0; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
### retrieve html |
70
|
|
|
|
|
|
|
my $html = $self->getHTML( $self->url ); |
71
|
|
|
|
|
|
|
# debug |
72
|
|
|
|
|
|
|
if($self->debug){ |
73
|
|
|
|
|
|
|
open(OUT,'>',$dbg->{html}) or confess $!; |
74
|
|
|
|
|
|
|
print OUT $html."\n\n"; |
75
|
|
|
|
|
|
|
print OUT $self->url."\n"; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
### html parsing |
78
|
|
|
|
|
|
|
my $list = $self->makeItemList($html); |
79
|
|
|
|
|
|
|
if(scalar @{$list} < 1){ |
80
|
|
|
|
|
|
|
$self->updateStatus("makeItemList returned with 0 item - html parse failure"); |
81
|
|
|
|
|
|
|
return 0; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
# debug |
84
|
|
|
|
|
|
|
if($self->debug){ |
85
|
|
|
|
|
|
|
open(OUT,'>',$dbg->{list}) or confess $!; |
86
|
|
|
|
|
|
|
require 'Dumpvalue.pm'; |
87
|
|
|
|
|
|
|
select(OUT); |
88
|
|
|
|
|
|
|
print Dumpvalue->new->dumpValue($list); |
89
|
|
|
|
|
|
|
select(STDOUT); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
### caching |
92
|
|
|
|
|
|
|
my ($update,$old_list,$size_new,$size_old) = $self->cache($list); |
93
|
|
|
|
|
|
|
# debug |
94
|
|
|
|
|
|
|
if($self->debug){ |
95
|
|
|
|
|
|
|
if($update){ |
96
|
|
|
|
|
|
|
open(OUT,'>',$dbg->{update}) or confess $!; |
97
|
|
|
|
|
|
|
print OUT "new: $size_new\nold: $size_old\n"; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
### read & parse old rss file |
101
|
|
|
|
|
|
|
my $rss_old = $self->_loadOldRss(); |
102
|
|
|
|
|
|
|
### remake RSS if update |
103
|
|
|
|
|
|
|
if($update){ |
104
|
|
|
|
|
|
|
my ($rss_new,$new_count) = $self->remakeRSS($list,$old_list,$rss_old); |
105
|
|
|
|
|
|
|
$self->rssObj($rss_new); |
106
|
|
|
|
|
|
|
# debug |
107
|
|
|
|
|
|
|
if($self->debug){ |
108
|
|
|
|
|
|
|
open(OUT,'>',$dbg->{newcount}) or confess $!; |
109
|
|
|
|
|
|
|
print OUT "$new_count\n"; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
$self->updateStatus("updated with $new_count new items"); |
112
|
|
|
|
|
|
|
return 1; |
113
|
|
|
|
|
|
|
}else{ |
114
|
|
|
|
|
|
|
$self->rssObj($rss_old); |
115
|
|
|
|
|
|
|
$self->updateStatus("there was no new item"); |
116
|
|
|
|
|
|
|
return 0; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub checkInterval { |
121
|
|
|
|
|
|
|
my $self = shift; |
122
|
|
|
|
|
|
|
my $cache_file = $self->_getCacheFilePath(); |
123
|
|
|
|
|
|
|
return 1 if(!-f $cache_file); |
124
|
|
|
|
|
|
|
return 1 if(!$self->minInterval); |
125
|
|
|
|
|
|
|
my $okTime = ( stat($cache_file) )[9] + $self->minInterval; |
126
|
|
|
|
|
|
|
my $nowTime = time(); |
127
|
|
|
|
|
|
|
return (1,$okTime,$nowTime) if($nowTime > $okTime); |
128
|
|
|
|
|
|
|
return (0,$okTime,$nowTime); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub getHTML { |
132
|
|
|
|
|
|
|
my $self = shift; |
133
|
|
|
|
|
|
|
my $url = shift; |
134
|
|
|
|
|
|
|
my $ua = LWP::UserAgent->new; |
135
|
|
|
|
|
|
|
$ua->cookie_jar({ file => $self->cacheDir.'/'.$self->name.'.cookie' }); |
136
|
|
|
|
|
|
|
my $res = $ua->get($url); |
137
|
|
|
|
|
|
|
confess q(couldn't retrieve html from ) . $url if(!$res->content); |
138
|
|
|
|
|
|
|
return $res->content; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub cache { |
142
|
|
|
|
|
|
|
my $self = shift; |
143
|
|
|
|
|
|
|
my $list = shift; |
144
|
|
|
|
|
|
|
my $cache_file = $self->_getCacheFilePath(); |
145
|
|
|
|
|
|
|
my $dump = Data::Dumper::Dumper($list); |
146
|
|
|
|
|
|
|
my $len_new = bytes::length($dump); |
147
|
|
|
|
|
|
|
my $len_old = -s $cache_file || 0; |
148
|
|
|
|
|
|
|
# if there's an update |
149
|
|
|
|
|
|
|
if($len_new != $len_old){ |
150
|
|
|
|
|
|
|
my $fh; |
151
|
|
|
|
|
|
|
# read old cache file |
152
|
|
|
|
|
|
|
my $old_data; |
153
|
|
|
|
|
|
|
if(-f $cache_file){ |
154
|
|
|
|
|
|
|
open($fh,'<',$cache_file) |
155
|
|
|
|
|
|
|
or confess "failed to open $cache_file - $!"; |
156
|
|
|
|
|
|
|
{ |
157
|
|
|
|
|
|
|
local ($/) = undef; |
158
|
|
|
|
|
|
|
my $x = <$fh>; |
159
|
|
|
|
|
|
|
($x) = ($x =~ /(.+)/ms); # untaint |
160
|
|
|
|
|
|
|
my $VAR1; |
161
|
|
|
|
|
|
|
$old_data = eval($x); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
close($fh); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
# make new cache file |
166
|
|
|
|
|
|
|
if($self->outFileName){ |
167
|
|
|
|
|
|
|
my $n = $self->outFileName; |
168
|
|
|
|
|
|
|
$cache_file =~ s|[^/]+(\..+?)$|$n$1|o; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
open($fh,'>',$cache_file) |
171
|
|
|
|
|
|
|
or confess "failed to write-open $cache_file - $!"; |
172
|
|
|
|
|
|
|
print $fh Data::Dumper::Dumper($list); |
173
|
|
|
|
|
|
|
return (1,$old_data,$len_new,$len_old); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
# else then there's no update |
176
|
|
|
|
|
|
|
return undef; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub remakeRSS { |
180
|
|
|
|
|
|
|
my $self = shift; |
181
|
|
|
|
|
|
|
my ($newlist,$oldlist,$oldrss) = @_; |
182
|
|
|
|
|
|
|
my $rss_new = new XML::RSS(%{ $self->passthru }); |
183
|
|
|
|
|
|
|
# if old rss hold no items, which means the file was broken or removed, |
184
|
|
|
|
|
|
|
# then we should reset the old list too, to remake all items again |
185
|
|
|
|
|
|
|
if( scalar @{$oldrss->{items} || []} == 0 ){ |
186
|
|
|
|
|
|
|
$oldlist = []; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
# find which item's new |
189
|
|
|
|
|
|
|
my (@new,%chk,%chkInOldRss); |
190
|
|
|
|
|
|
|
# making check hash |
191
|
|
|
|
|
|
|
my $i=0; |
192
|
|
|
|
|
|
|
foreach (@{ $oldlist }){ |
193
|
|
|
|
|
|
|
$chk{ $_->{link} } = $i; |
194
|
|
|
|
|
|
|
$i++; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
# making check hash - for items only exist in rss file, and not in cache |
197
|
|
|
|
|
|
|
$i=0; |
198
|
|
|
|
|
|
|
foreach (@{ $oldrss->{items} }){ |
199
|
|
|
|
|
|
|
next if($chk{ $_->{link} }); # ignore those in cache |
200
|
|
|
|
|
|
|
$chkInOldRss{ $_->{link} } = $i; |
201
|
|
|
|
|
|
|
$i++; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
foreach my $p (@{ $newlist }){ |
204
|
|
|
|
|
|
|
# check for any content updates, compared to cache list |
205
|
|
|
|
|
|
|
if(exists $chk{ $p->{link} }){ |
206
|
|
|
|
|
|
|
my $o = $oldlist->[ $chk{ $p->{link} } ]; |
207
|
|
|
|
|
|
|
my $oldlen = bytes::length(Data::Dumper::Dumper($o)); |
208
|
|
|
|
|
|
|
my $newlen = bytes::length(Data::Dumper::Dumper($p)); |
209
|
|
|
|
|
|
|
if($newlen != $oldlen){ |
210
|
|
|
|
|
|
|
# delete that old item from rss |
211
|
|
|
|
|
|
|
my @tmp; |
212
|
|
|
|
|
|
|
my $qr = qr/\Q$p->{link}\E/; |
213
|
|
|
|
|
|
|
foreach my $old (@{ $oldrss->{items} }){ |
214
|
|
|
|
|
|
|
push(@tmp,$old) unless($old->{link} =~ /$qr/); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
$oldrss->{items} = \@tmp; |
217
|
|
|
|
|
|
|
push(@new,$p); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
# else, check for duplicates |
220
|
|
|
|
|
|
|
}elsif(exists $chkInOldRss{ $p->{link} }){ |
221
|
|
|
|
|
|
|
my @tmp; |
222
|
|
|
|
|
|
|
my $qr = qr/\Q$p->{link}\E/; |
223
|
|
|
|
|
|
|
foreach my $itm (@{ $oldrss->{items} }){ |
224
|
|
|
|
|
|
|
push(@tmp,$itm) unless($itm->{link} =~ /$qr/); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
$oldrss->{items} = \@tmp; |
227
|
|
|
|
|
|
|
push(@new,$p); |
228
|
|
|
|
|
|
|
# if it's a brand new item |
229
|
|
|
|
|
|
|
}else{ |
230
|
|
|
|
|
|
|
push(@new,$p); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
# make rss for new items |
234
|
|
|
|
|
|
|
my $new_count = 0; |
235
|
|
|
|
|
|
|
for (my $i=0; $i < scalar @new; $i++){ |
236
|
|
|
|
|
|
|
last if (defined($self->maxItemCount) && $i == $self->maxItemCount); |
237
|
|
|
|
|
|
|
$self->addNewItem($rss_new,$new[$i]); |
238
|
|
|
|
|
|
|
$new_count++; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
# add old items |
241
|
|
|
|
|
|
|
my $now = scalar @new; |
242
|
|
|
|
|
|
|
foreach my $itr (@{ $oldrss->{items} }){ |
243
|
|
|
|
|
|
|
last if (defined($self->maxItemCount) && $now >= $self->maxItemCount); |
244
|
|
|
|
|
|
|
$rss_new->add_item(%{$itr}); |
245
|
|
|
|
|
|
|
$now++; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
# set RSS definition |
248
|
|
|
|
|
|
|
$self->defineRSS($rss_new); |
249
|
|
|
|
|
|
|
# save to file |
250
|
|
|
|
|
|
|
$self->_saveToFile($rss_new); |
251
|
|
|
|
|
|
|
# set to $self->newItems property |
252
|
|
|
|
|
|
|
my @newItems; |
253
|
|
|
|
|
|
|
for (my $i=0; $i < scalar @new; $i++){ |
254
|
|
|
|
|
|
|
push(@newItems,$rss_new->{items}[$i]); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
$self->newItems(\@newItems); |
257
|
|
|
|
|
|
|
return ($rss_new,$new_count); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub as_string { |
261
|
|
|
|
|
|
|
my $self = shift; |
262
|
|
|
|
|
|
|
$self->_loadOldRss if(!$self->rssObj); |
263
|
|
|
|
|
|
|
return $self->rssObj->as_string(); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub as_object { |
267
|
|
|
|
|
|
|
my $self = shift; |
268
|
|
|
|
|
|
|
$self->_loadOldRss if(!$self->rssObj); |
269
|
|
|
|
|
|
|
return $self->rssObj; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub name { |
273
|
|
|
|
|
|
|
my $self = shift; |
274
|
|
|
|
|
|
|
if(@_){ |
275
|
|
|
|
|
|
|
my $s = shift; |
276
|
|
|
|
|
|
|
$s =~ s/[^a-zA-z0-9\-]/_/g; |
277
|
|
|
|
|
|
|
$self->{name} = $s; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
return $self->{name}; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub getDateTime { |
283
|
|
|
|
|
|
|
my $self = shift; |
284
|
|
|
|
|
|
|
my $str = shift; |
285
|
|
|
|
|
|
|
my $t; |
286
|
|
|
|
|
|
|
require HTTP::Date; |
287
|
|
|
|
|
|
|
if($str){ |
288
|
|
|
|
|
|
|
$t = HTTP::Date::str2time($str); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
return HTTP::Date::time2str($t); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub _loadOldRss { |
294
|
|
|
|
|
|
|
my $self = shift; |
295
|
|
|
|
|
|
|
my $file = $self->_getFeedFilePath(); |
296
|
|
|
|
|
|
|
my $r = XML::RSS->new(%{ $self->{passthru} }); |
297
|
|
|
|
|
|
|
eval { |
298
|
|
|
|
|
|
|
$r->parsefile($file) if(-f $file); |
299
|
|
|
|
|
|
|
}; |
300
|
|
|
|
|
|
|
if( $@ || scalar( @{$r->{items} || []} ) < 1 ){ |
301
|
|
|
|
|
|
|
$self->updateStatus("old rss file was broken, so ignoring - $@"); |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
if($self->unicodeDowngrade){ |
304
|
|
|
|
|
|
|
eval { require Unicode::RecursiveDowngrade }; |
305
|
|
|
|
|
|
|
if( $@ ){ |
306
|
|
|
|
|
|
|
warn 'you will need to install Unicode::RecursiveDowngrade module to use $self->unicodeDowngrade option'; |
307
|
|
|
|
|
|
|
}else{ |
308
|
|
|
|
|
|
|
$r = Unicode::RecursiveDowngrade->new->downgrade($r); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
$self->rssObj($r); |
312
|
|
|
|
|
|
|
return $r; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub _getCacheFilePath { |
316
|
|
|
|
|
|
|
my $self = shift; |
317
|
|
|
|
|
|
|
return $self->cacheDir.'/'.$self->name.'.cache'; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub _getFeedFilePath { |
321
|
|
|
|
|
|
|
my $self = shift; |
322
|
|
|
|
|
|
|
return $self->feedDir.'/'.$self->name.'.xml'; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub _saveToFile { |
326
|
|
|
|
|
|
|
my $self = shift; |
327
|
|
|
|
|
|
|
my $rss_new = shift; |
328
|
|
|
|
|
|
|
my $saveFile = $self->_getFeedFilePath(); |
329
|
|
|
|
|
|
|
if($self->outFileName){ |
330
|
|
|
|
|
|
|
my $n = $self->outFileName; |
331
|
|
|
|
|
|
|
my ($name, $dir, $suffix) = File::Basename::fileparse( $saveFile, qr/\.[^.]*/ ); |
332
|
|
|
|
|
|
|
$saveFile = "$dir$n$suffix"; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
$rss_new->save( $saveFile ) or confess $!; |
335
|
|
|
|
|
|
|
return 1; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# below are all must-override methods |
339
|
|
|
|
|
|
|
sub init { |
340
|
|
|
|
|
|
|
confess q( |
341
|
|
|
|
|
|
|
must override this method with sub-class using the following interface: |
342
|
|
|
|
|
|
|
sub init { |
343
|
|
|
|
|
|
|
my $self = shift; |
344
|
|
|
|
|
|
|
# set feed url, name, and other constant stuff here # |
345
|
|
|
|
|
|
|
$self->url('http://target.site/updates.html'); |
346
|
|
|
|
|
|
|
$self->name('sample feed'); |
347
|
|
|
|
|
|
|
$self->passthru({ |
348
|
|
|
|
|
|
|
version => '1.0', |
349
|
|
|
|
|
|
|
encode_output => 1, |
350
|
|
|
|
|
|
|
}); |
351
|
|
|
|
|
|
|
return 1; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub makeItemList { |
357
|
|
|
|
|
|
|
confess q( |
358
|
|
|
|
|
|
|
must override this method with sub-class using the following interface: |
359
|
|
|
|
|
|
|
sub makeItemList { |
360
|
|
|
|
|
|
|
my $self = shift; |
361
|
|
|
|
|
|
|
my $html = shift; |
362
|
|
|
|
|
|
|
my @list; |
363
|
|
|
|
|
|
|
# parse html and make an item list here # |
364
|
|
|
|
|
|
|
while ($html =~ /(.+?)/){ |
365
|
|
|
|
|
|
|
push(@list,{ |
366
|
|
|
|
|
|
|
link => $1, |
367
|
|
|
|
|
|
|
title => $2, |
368
|
|
|
|
|
|
|
}); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
return \@list; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub addNewItem { |
376
|
|
|
|
|
|
|
confess q( |
377
|
|
|
|
|
|
|
must override this method with sub-class using the following interface: |
378
|
|
|
|
|
|
|
sub addNewItem { |
379
|
|
|
|
|
|
|
my $self = shift; |
380
|
|
|
|
|
|
|
my ($rssObject,$item) = @_; |
381
|
|
|
|
|
|
|
# create & add new item to rssObject using data in # |
382
|
|
|
|
|
|
|
# $item hashRef, which you made in makeItemList() # |
383
|
|
|
|
|
|
|
$rssObject->add_item( |
384
|
|
|
|
|
|
|
link => $item->{link}, |
385
|
|
|
|
|
|
|
title => $item->{title}, |
386
|
|
|
|
|
|
|
); |
387
|
|
|
|
|
|
|
return 1; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub defineRSS { |
393
|
|
|
|
|
|
|
confess q( |
394
|
|
|
|
|
|
|
must override this method with sub-class using the following interface: |
395
|
|
|
|
|
|
|
sub defineRSS { |
396
|
|
|
|
|
|
|
my $self = shift; |
397
|
|
|
|
|
|
|
my $rssObject = shift; |
398
|
|
|
|
|
|
|
# define rss channel info, and other stuffs here # |
399
|
|
|
|
|
|
|
$rssObject->channel( |
400
|
|
|
|
|
|
|
title => 'blabla rss feed', |
401
|
|
|
|
|
|
|
description => 'foo bar', |
402
|
|
|
|
|
|
|
link => 'http://mysite/rss/', |
403
|
|
|
|
|
|
|
); |
404
|
|
|
|
|
|
|
$rssObject->image( |
405
|
|
|
|
|
|
|
title => "blabla rss feed", |
406
|
|
|
|
|
|
|
url => "http://mysite/rss/feed.png", |
407
|
|
|
|
|
|
|
); |
408
|
|
|
|
|
|
|
return 1; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
); |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
1; |
414
|
|
|
|
|
|
|
__END__ |