online rss & atom feed reader for gemini
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

130 lines
3.3 KiB

#!/usr/bin/perl
# Copyright René Wagner 2020
# licenced under BSD 3-Clause licence
# https://src.clttr.info/rwa/orrg
use strict;
use URI::Escape;
use XML::FeedPP;
use HTML::Strip;
use DateTime;
use DateTime::Format::ISO8601;
use POSIX qw(strftime);
use lib 'lib/';
use orrg;
# enable UTF-8 mode for everything
use utf8;
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
if (!defined($ENV{'SERVER_PROTOCOL'}) || $ENV{'SERVER_PROTOCOL'} ne 'GEMINI')
{
write_response('CGI_ERROR', '', undef);
}
my $query = lc(uri_unescape($ENV{'QUERY_STRING'}));
if ($query eq '' || $query !~ /^(https|gemini)\:\/\//) {
write_response('INPUT', 'Paste the URI of the rss feed you want to read', undef);
}
write_response('SUCCESS', 'text/gemini', create_response($query));
exit;
sub create_response
{
my ( $qs ) = @_;
my @body = ();
my $content = feed_get($qs);
if ( !defined($content) || $content eq '' ) {
push @body, ('# orrg error', '', 'The requested feed could not be loaded. :(', '', '=> '. $qs .' open feed in browser');
} else {
my $feed = feed_parse($content);
if (defined($feed)) {
recent_add($qs, trim_ws($feed->title));
popular_add($qs, trim_ws($feed->title));
push @body, '# '. trim_ws($feed->title);
push @body, 'fetched '. strftime('%Y-%m-%dT%H:%M:%SZ', gmtime());
push @body, '';
$feed->description eq '' or push @body, trim_ws($feed->description);
$feed->image eq '' or push @body, '=> '. $feed->image .' feed image';
$feed->link eq '' or push @body, ('=> '.$feed->link.' open website', '');
foreach my $it ($feed->get_item()) { push @body, @{item($it)}; }
} else {
push @body, ('# orrg error', '', 'The requested feed could be loaded but not parsed. :(', '', '=> '. $qs .' open feed in native client');
}
}
push @body, ('', '', '=> ./index.pl [home]');
return @body;
}
sub item
{
my ($it) = @_;
my $hs = HTML::Strip->new(emit_spaces => 0, auto_reset => 1);
my @item = ();
my $title = trim_ws($it->title);
my $link = trim_ws($it->link);
my $desc = $it->description;
if ($desc ne '') {
$desc =~ s/\<li\>/* /igm;
$desc =~ s/\<h[1-2][^\>]+\>/### /igm;
$desc =~ s/\<br \/\>/\r\n/igm;
$desc =~ s/\<pre.*\>/\r\n```\r\n/igm;
$desc =~ s/\<\/pre\>/\r\n```\r\n/igm;
$desc = $hs->parse($desc);
}
push @item, (($desc ne '' && $desc ne $title) || $it->pubDate ne '')? '## '. $title : $title;
if ($it->pubDate ne '') {
my $dt = DateTime::Format::ISO8601->parse_datetime($it->pubDate);
push @item, 'published '. strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($dt->epoch));
push @item, '';
}
if ($desc ne '' && $desc ne $title) {
push @item, $desc;
}
$link eq '' or push @item, ('=> '.$link.' open entry in browser', '');
return \@item;
}
sub trim_ws
{
my ( $string ) = @_;
$string =~ s/^\s+\*/*/igm;
$string =~ s/\s+$//igm;
$string =~ s/^\s+//igm;
$string =~ s/^\r\n$//igm;
return $string;
}
sub feed_get
{
my ( $query ) = @_;
my $content;
if ( $query =~ /^https\:\/\// ) { $content = `wget -qO - $query`; }
if ( $query =~ /^gemini\:\/\// ) {
$content = `./gcat $query`;
$content =~ /20\W/ or return undef;
$content =~ s/^[0-9]{0,2}\W.+\r\n//;
}
return $content;
}
sub feed_parse
{
my ($content) = @_;
my $feed = XML::FeedPP->new($content, -type => 'string', utf8_flag => 1);
if (defined($feed)) { $feed->sort_item(); }
return $feed;
}