A while ago I went looking for good rss / atom readers. I had a few requirements:
- a terminal based user interface
- keeps track of what you have/haven't read
- archive everything so you can read offline
- ability group rss feeds by category
- lightweight, preferably with no dependencies I don't already have installed
The first software I found that might fit my requirements was Newsboat. It has a lot of features and is actively maintained. The main problem with newsboat is that it has too many features. A filter language, killfiles (?), query feeds, podcast support, interfaces for multiple online feedreaders, etc. I.e. a bunch of bloat that I will never use. To top it all off, it's written in C++. Funnily enough, in the midst of all those features I couldn't find a way to download multiple feeds in parallel. Every time I wanted to fetch new feeds it sequentially downloaded each one in my list. Another anti-feature was the lack of feed groups. To be fair I didn't spend that much time with the software so I may have just missed those two features, but it was already too late for Newsboat.
Next, I took a look at snownews. I didn't actually compile it, but after a cursory review, I deemed it too complicated as well. Why did it take around 7K lines of C code (including http digest handling code?!) to accomplish what should be a simple task? I think that snownews tries to do to much, when there are already great tools written to do things like download files and render html to the terminal. I knew there was a better way.
So, about 4 hours later, I came up with rss,
a barebones feed reader. Its dependencies are
curl, to fetch the feed,
xsltproc, to parse the feed, and
w3m to render the feed's posts. Of those
curl and xsltproc are pretty universal. Additionally w3m is likely to be
installed by heavy terminal users already, so I didn't consider depending on it
a deal breaker. If someone knows a better html viewer for the terminal, let me
know. Also, rss's manpage is generated by
scdoc, but that counts as an
optional dependency :^).
All of the heavy lifting is done by xsltproc and this stylesheet. xslt was an interesting language to work with, but it was definitely the right tool for the job. With just a 60 line stylesheet I could seperate a single xml feed into a file / directory structure like this:
Ruby News `-- 2019 |-- 08 | `-- 28 | |-- 09:00:00 - Ruby 2.5.6 Released | `-- 09:00:00 - Ruby 2.6.4 Released `-- 10 |-- 01 | |-- 11:00:00 - CVE-2019-15845: A NUL injection vulnerability of File.fnmatch and Fil | |-- 11:00:00 - CVE-2019-16201: Regular Expression Denial of Service vulnerability of | |-- 11:00:00 - CVE-2019-16254: HTTP response splitting in WEBrick (Additional fix) | |-- 11:00:00 - CVE-2019-16255: A code injection vulnerability of Shell# and Shell# | |-- 11:00:00 - Ruby 2.4.8 Released | |-- 11:00:00 - Ruby 2.5.7 Released | `-- 11:00:00 - Ruby 2.6.5 Released `-- 02 `-- 09:00:00 - Ruby 2.4.9 Released
The remainder of the features are implemented with basic unix utilities. For
instance, the "read" list, is just a list of paths that gets appended to as you
read posts. In order to find unread posts, a list of all posts is generated
catenated with the "read" list,
I was able to cut down on complexity in other places too. In order to specify
the feeds you want to subscribe to, you write a shell script that calls the
feed. rss will source that script when necessary and the resulting
feed actually fetch the feed directly.
It was also trivial to add parallel fetching using shell job control. I was
able to enforce a limit on concurrent jobs using the
jobs builtin and
while [ $(jobs -p | wc -l) -ge $job_limit ]; do sleep 0.5 done
The final innovation was to allow rss to be extensible. This was accomplished
by having a second shell script that gets sourced on startup. Functions defined
in the extension script that begin with
rss_ can be called from the command
line. These functions also have the entirety of rss to call themselves.
Using the primitives provided by rss it is easy to implement a command that
fetches and then lists new posts, or fetches with a different concurrent limit.
All of these features are accomplished in around 200 lines of POSIX shell script, so if your interest is piqued, then feel free to dive in. Use it as is or as a starting point for your own ultimate feed reader! I've been using rss with a few extension functions for a couple of months now and so far it has accomplished everything I need.
Note: as of writing the provided zsh completions sort of work and the bash completions don't at all. Submit a patch!
In case you missed it, here is a link to the repo: https://git.sr.ht/~lattis/rss