txt2pre: '<updated>' in '<entry>' is required in atom feeds
[~bandali/bndl.org] / txt2pre
diff --git a/txt2pre b/txt2pre
index e288a69..1f04eb4 100644 (file)
--- a/txt2pre
+++ b/txt2pre
@@ -1,5 +1,5 @@
 #!/usr/bin/env perl
-# txt2pre --- convert my site's txt files to `pre'-based html
+# txt2pre --- convert my site's txt files to `pre'-based atom/rss/html
 
 # Copyright (C) 2014-2021 all contributors <meta@public-inbox.org>
 # Copyright (c) 2021 Amin Bandali <bandali@gnu.org>
@@ -26,12 +26,40 @@ use strict;
 use warnings 'all';
 use Getopt::Long;
 
-my $opt_lang = 'en';
-my $opt_index;
-GetOptions ('lang=s' => \$opt_lang,
-            'index' => \$opt_index)
+my $format = 'html';
+my $lang = 'en';
+my $index = '';
+my $header = '';
+my $footer = '';
+
+GetOptions ('format=s' => \$format,
+            'lang=s' => \$lang,
+            'index' => \$index,
+            'header' => \$header,
+            'footer' => \$footer)
     or die("bad command line arguments\n");
 
+my $author =
+    $lang eq 'en' ? 'bandali'
+    : $lang eq 'fa' ? 'بندعلی'
+    : '';
+my $site_title =
+    $lang eq 'en' ? "${author}'s personal site"
+    : $lang eq 'fa' ? "سایت شخصی $author"
+    : '';
+my $site_desc =
+    $lang eq 'en' ? "notes and blog posts by $author"
+    : $lang eq 'fa' ? "نوشته‌ها و بلاگ پست‌های $author"
+    : '';
+my $site_url =
+    ($lang eq 'en') ? 'https://bndl.org'
+    : ($lang eq 'fa') ? 'https://bndl.org/fa/'
+    : '';
+my $feed_id =
+    ($lang eq 'en') ? "tag:bndl.org,2020:notes.$format"
+    : ($lang eq 'fa') ? "tag:bndl.org,2020:fa/notes.$format"
+    : '';
+
 my $link_re =
     qr{([\('!])?\b((?:ftps?|https?|nntps?|imaps?|s?news|gopher)://
        [\@:\w\.-]+(?:/
@@ -96,19 +124,83 @@ sub linkify {
 }
 
 
+my $out = '';
+
+# atom/rss feed header and footer
+if ($index and ($format eq 'atom' or $format eq 'rss')) {
+    if ($header) {
+        my $now_iso8601 = `date -Iseconds -u | tr -d \\\\n`;
+        my $now_rfc5322 = `date -uR | tr -d \\\\n`;
+        my $atom_rel = $format eq 'atom' ? 'self' : 'alternate';
+        my $rss_rel = $format eq 'rss' ? 'self' : 'alternate';
+        my $link = $format eq 'atom' ? 'link' : 'atom:link';
+        my $links = '';
+        if ($lang eq 'en') {
+            $links = qq(
+<$link hreflang="fa" href="https://bndl.org/fa/notes.atom" rel="alternate" type="application/atom+xml" />
+<$link hreflang="fa" href="https://bndl.org/fa/notes.rss" rel="alternate" type="application/rss+xml" />
+<$link hreflang="fa" href="https://bndl.org/fa/bandali.fa.txt" rel="alternate" type="text/plain" />
+<$link hreflang="fa" href="https://bndl.org/fa/" rel="alternate" type="text/html" />
+<$link href="https://bndl.org/notes.atom" rel="$atom_rel" type="application/atom+xml" />
+<$link href="https://bndl.org/notes.rss" rel="$rss_rel" type="application/rss+xml" />
+<$link href="https://bndl.org/bandali.txt" rel="alternate" type="text/plain" />
+<$link href="https://bndl.org" rel="alternate" type="text/html" />);
+        } elsif ($lang eq 'fa') {
+            $links = qq(
+<link hreflang="en" href="https://bndl.org/notes.atom" rel="alternate" type="application/atom+xml" />
+<link hreflang="en" href="https://bndl.org/notes.rss" rel="alternate" type="application/rss+xml" />
+<link hreflang="en" href="https://bndl.org/bandali.txt" rel="alternate" type="text/plain" />
+<link hreflang="en" href="https://bndl.org" rel="alternate" type="text/html" />
+<link href="https://bndl.org/fa/notes.atom" rel="$atom_rel" type="application/atom+xml" />
+<link href="https://bndl.org/fa/notes.rss" rel="$rss_rel" type="application/rss+xml" />
+<link href="https://bndl.org/fa/bandali.fa.txt" rel="alternate" type="text/plain" />
+<link href="https://bndl.org/fa/" rel="alternate" type="text/html" />);
+        }
+        $links =~ s/^\n//;
+
+        $out .= '<?xml version="1.0" encoding="UTF-8" ?>';
+        $out .= ($format eq 'atom') ? qq(
+<feed xml:lang="$lang" xmlns="http://www.w3.org/2005/Atom">
+<title>$site_title</title>
+<subtitle>$site_desc</subtitle>
+<id>$feed_id</id>
+$links
+<updated>$now_iso8601</updated>)
+            : ($format eq 'rss') ? qq(
+<rss version="2.0"
+     xmlns:atom="http://www.w3.org/2005/Atom"
+     xmlns:content="http://purl.org/rss/1.0/modules/content/">
+<channel>
+<title>$site_title</title>
+<description>$site_desc</description>
+<link>$site_url</link>
+<language>$lang</language>
+<lastBuildDate>$now_rfc5322</lastBuildDate>
+<pubDate>$now_rfc5322</pubDate>
+<ttl>1800</ttl>
+$links)
+            : '';
+    } elsif ($footer) {
+        $out .= ($format eq 'atom') ? '</feed>'
+            : ($format eq 'rss') ? '</channel></rss>'
+            : '';
+    }
+
+    # we're done
+    goto PRINT;
+}
+
+
 my $txt = do { local $/; <STDIN> };
 
 my $title = html_esc($txt =~ /\A([^\n]+)/);
 $title =~ s/^\s+|\s+$//g;
-if ($opt_lang eq 'fa') {
-    $title .= ' &mdash; بندعلی' if $title !~ /بندعلی/;
-} else {
-    $title .= ' &mdash; bandali' if $title !~ /bandali/;
-}
+$title .= " &#8212; $author" if $title !~ /$author/;
 
 my ($upd, $pub, $url) = $txt =~ /(.*)\r?\n(.*)\r?\n(.*)\r?\n?\z/;
 ($upd) = $upd =~ /(?:updated|ویرایش): (.*)/ if $upd;
 ($pub) = $pub =~ /(?:published|انتشار): (.*)/ if $pub;
+$upd = $pub if (!$upd);
 ($url) = $url =~ /(?:plain text|متن ساده): (.*)/ if $url;
 $url = 'https://bndl.org/bandali-cv.txt'
     if (!$url and $title =~ /curriculum vitae/);
@@ -116,32 +208,86 @@ $url = html_esc($url) if $url;
 
 $txt = linkify(html_esc($txt));
 
-print("<!doctype html>",
-      qq(<html lang="$opt_lang"),
-      $opt_lang eq 'fa' ? ' dir="rtl"' : '',
-      ">",
-      qq(<head>
+
+my $upd_iso8601 = `date -Iseconds -ud '$upd' | tr -d \\\\n` if $upd;
+my $pub_iso8601 = `date -Iseconds -ud '$pub' | tr -d \\\\n` if $pub;
+my $pub_rfc5322 = `date -uRd '$pub' | tr -d \\\\n` if $pub;
+my $url_html = $url =~ s/(?:[.]$lang)?[.]txt$/.html/r if $url;
+$url_html =~ s|/bandali-(.*)|/$1| if $url_html;
+my $slug = $url_html =~ s|.*/(.*)[.]html$|$1|r if $url_html;
+my $note_id = "$feed_id:$slug" if $url_html;
+
+# note header
+if ($format eq 'html') {
+    $out .=
+        '<!doctype html>'
+        . qq(<html lang="$lang") . ($lang eq 'fa'
+                                    ? ' dir="rtl"'
+                                    : '' . '>')
+        . qq(<head>
 <meta http-equiv="Content-Type"
-content="text/html; charset=utf-8" />\n),
-      "<title>$title</title>\n",
-      qq(<link rel="icon" href="data:,">\n),
-      $url ? qq(<link rel="alternate" href="$url"
-title="plain text" type="text/plain" />\n) : '',
-      ($opt_index and $opt_lang eq 'en')
-      ? qq(<link rel="alternate" href="https://bndl.org/fa/"
+content="text/html; charset=utf-8" />\n)
+        . "<title>$title</title>\n"
+        . qq(<link rel="icon" href="data:,">\n)
+        . ($url
+           ? qq(<link rel="alternate" href="$url"
+title="plain text" type="text/plain" />\n)
+           : '')
+        . (($index and $lang eq 'en')
+           ? qq(<link rel="alternate" href="https://bndl.org/fa/"
 hreflang="fa" title="persian" />\n)
-      : ($opt_index and $opt_lang eq 'fa')
-      ? qq(<link rel="alternate" href="https://bndl.org/"
+           : ($index and $lang eq 'fa')
+           ? qq(<link rel="alternate" href="https://bndl.org/"
 hreflang="en" title="english" />\n)
-      : '',
-      qq(<style>\@media(prefers-color-scheme:dark){
+           : '')
+        . qq(<style>\@media(prefers-color-scheme:dark){
 body{background:#1c1c1c;color:white;}a:link{color:#acdeff;}
-a:visited{color:#f8f;}a:active{color:#e00;}}),
-      $opt_lang eq 'fa'
-      ? qq(\n\@font-face{font-family:sahel;font-weight:normal;
+a:visited{color:#f8f;}a:active{color:#e00;}})
+        . ($lang eq 'fa'
+           ? qq(\n\@font-face{font-family:sahel;font-weight:normal;
 src:local('Sahel WOL'),local('Sahel'),
 url('sahel.woff2')format('woff2');}pre{font-family:sahel})
-      : '',
-      "</style>\n",
-      "</head><body><pre>$txt</pre></body></html>\n");
+           : '')
+        . "</style>\n"
+        . '</head><body><pre>';
+} elsif ($format eq 'atom' or $format eq 'rss') {
+    my $atom_updated =
+        ($format eq 'atom') ? 'updated'
+        : ($format eq 'rss') ? 'atom:updated'
+        : '';
+    my $updated =
+        "<$atom_updated>$upd_iso8601</$atom_updated>\n" if $upd;
+    $out .= ($format eq 'atom') ? qq(
+<entry xml:base="$site_url">
+<author><name>$author</name></author>
+<id>$note_id</id>
+<published>$pub_iso8601</published>\n)
+. ($updated ? $updated : '') .
+qq(<link href="$url" rel="alternate" type="text/plain" />
+<link href="$url_html" rel="alternate" type="text/html" />
+<title>$title</title>
+<content type="html"><![CDATA[<pre>)
+        : ($format eq 'rss') ? qq(
+<item>
+<title>$title</title>
+<link>$url_html</link>
+<guid isPermaLink="false">$note_id</guid>
+<pubDate>$pub_rfc5322</pubDate>\n)
+. (($updated and $pub ne $upd) ? $updated : '') .
+qq(<content:encoded><![CDATA[<pre>)
+        : '';
+}
+# note body
+$out .= $txt;
+# note footer
+if ($format eq 'html') {
+    $out .= '</pre></body></html>';
+} elsif ($format eq 'atom') {
+    $out .= "</pre>]]></content></entry>";
+} elsif ($format eq 'rss') {
+    $out .= "</pre>]]></content:encoded></item>";
+}
+
+ PRINT:
+print("$out\n");
 STDOUT->flush;