ref: 37acbad25a1283e2e4cc63ec8ba36482f0c4a45b
author: telephil9 <telephil9@gmail.com>
date: Thu Feb 6 15:14:54 EST 2020
Initial import
--- /dev/null
+++ b/README
@@ -1,0 +1,6 @@
+This is a fork of rrss (http://code.9front.org/hg/rrss) by Stanley Lieber.
+This version is modified to handle multiple feeds at once.
+When run the program expect a text files with the following format:
+<feed url> [tag1 [tag2 ... [tagN]]]
+
+For documentation refer to rrss.txt which is the original README of the program.
--- /dev/null
+++ b/main.go
@@ -1,0 +1,223 @@
+// RSS feed reader that outputs plain text, werc/apps/barf
+package main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/SlyMarbo/rss"
+)
+
+var (
+ debug = flag.Bool("d", false, "print debug msgs to stderr")
+ format = flag.String("f", "", "output format")
+ root = flag.String("r", "", "output root")
+ dest string
+ links string
+)
+
+type Article struct {
+ Title string
+ Link string
+ Date time.Time
+ Content string
+ Tags []string
+}
+
+type renderfn func(articles []Article)
+
+func usage() {
+ os.Stderr.WriteString("usage: rrss [-d] [-f barf|blagh] [-r root] <feed file>\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+func check(err error) {
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func isold(link string, path string) bool {
+ file, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0775)
+ if err != nil {
+ return true
+ }
+ defer file.Close()
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ if strings.Contains(link, scanner.Text()) {
+ return true
+ }
+ }
+ return false
+}
+
+func makeold(link string, path string) (int, error) {
+ f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0775)
+ defer f.Close()
+ check(err)
+ if link == "" {
+ link = "empty"
+ }
+ return f.WriteString(link + "\n")
+}
+
+func writef(dir, filename, content string) {
+ err := ioutil.WriteFile(path.Join(dir, filename), []byte(content+"\n"), 0775)
+ check(err)
+}
+
+func ensuredir(dir string) {
+ err := os.MkdirAll(dir, 0775)
+ check(err)
+}
+
+// http://code.9front.org/hg/barf
+func barf(articles []Article) {
+ dest = path.Join(*root, "src")
+ ensuredir(dest)
+ n := lastarticle(dest)
+ for _, a := range articles {
+ n = n + 1
+ d := fmt.Sprintf("%s/%d", dest, n)
+ ensuredir(d)
+ writef(d, "title", a.Title)
+ writef(d, "link", a.Link)
+ writef(d, "date", a.Date.String())
+ writef(d, "body", a.Content)
+ if a.Tags != nil {
+ ensuredir(path.Join(d, "tags"))
+ for _, j := range a.Tags {
+ f, err := os.Create(d + "/tags/" + j)
+ f.Close()
+ check(err)
+ }
+ }
+ _, err := makeold(a.Link, links)
+ check(err)
+ }
+}
+
+// http://werc.cat-v.org/apps/blagh
+func blagh(articles []Article) {
+ var err error
+ for _, a := range articles {
+ dest = path.Join(*root, fmt.Sprintf("%d/%02d/%02d", a.Date.Year(), a.Date.Month(), a.Date.Day()));
+ ensuredir(dest)
+ f, _ := os.Open(dest) // directory will usually not exist yet
+ defer f.Close()
+ n, _ := f.Readdirnames(0)
+ d := fmt.Sprintf("%s/%d", dest, len(n))
+ ensuredir(d)
+ writef(d, "index.md", fmt.Sprintf("%s\n===\n\n%s\n", a.Title, a.Content))
+ _, err = makeold(a.Link, links)
+ check(err)
+ }
+}
+
+func stdout(articles []Article) {
+ for _, a := range articles {
+ fmt.Printf("title: %s\nlink: %s\ndate: %s\n%s\n\n",
+ a.Title, a.Link, a.Date, a.Content)
+ }
+}
+
+func lastarticle(dir string) int {
+ f, err := os.Open(dir)
+ defer f.Close()
+ check(err)
+ dn, err := f.Readdirnames(0)
+ check(err)
+ var di []int
+ for _, j := range dn {
+ k, _ := strconv.Atoi(j)
+ di = append(di, k)
+ }
+ sort.Ints(di)
+ n := 0
+ if di != nil {
+ n = di[len(di)-1]
+ }
+ return n
+}
+
+func loadfeed(url string, tags []string) []Article {
+ var articles []Article
+ if *debug {
+ log.Printf("Fetching feed '%s'", url)
+ }
+ feed, err := rss.Fetch(url)
+ if err != nil {
+ log.Printf("Cannot load feed '%s': %v", url, err)
+ return nil
+ }
+ for _, i := range feed.Items {
+ if isold(i.Link, links) {
+ continue
+ }
+ a := Article{i.Title, i.Link, i.Date, conorsum(i), tags}
+ articles = append(articles, a)
+ }
+ if *debug {
+ log.Printf("Loaded %d items", len(articles))
+ }
+ return articles
+}
+
+func conorsum(i *rss.Item) string {
+ if len(i.Content) > 0 {
+ return i.Content
+ }
+ if len(i.Summary) > 0 {
+ return i.Summary
+ }
+ return ""
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ if flag.Arg(0) == "" {
+ usage()
+ }
+ var render renderfn
+ switch *format {
+ case "barf":
+ render = barf
+ case "blagh":
+ render = blagh
+ case "":
+ render = stdout
+ default:
+ usage()
+ }
+ links = path.Join(*root, "links")
+ file, err := os.Open(flag.Arg(0))
+ check(err)
+ defer file.Close()
+ scanner := bufio.NewScanner(file)
+ var articles []Article
+ var tags []string
+ for scanner.Scan() {
+ l := strings.Split(scanner.Text(), " ")
+ if len(l) > 1 {
+ tags = l[1:]
+ }
+ a := loadfeed(l[0], tags)
+ if a != nil {
+ articles = append(articles, a...)
+ }
+ }
+ sort.Slice(articles, func(i, j int) bool { return articles[i].Date.Before(articles[j].Date) })
+ render(articles)
+}
--- /dev/null
+++ b/rrss.txt
@@ -1,0 +1,41 @@
+ RRSS(1) RRSS(1)
+
+ NAME
+ rrss, trrss - RSS feed readers
+
+ SYNOPSIS
+ rrss [-f barf|blagh] [-r root] [-t tag] [-u url]
+
+ trrss [-f barf|blagh] [-r root] [-t tag] [-u url]
+
+ DESCRIPTION
+ Rrss pulls and parses an RSS feed.
+
+ There are a number of options:
+
+ -f Place output in formatted directories for one
+ of two werc apps: barf or blagh. In the absence
+ of the -f flag, formatted output is placed on
+ stdout.
+
+ A file, links, is created in the root and is populated
+ with the URL of each feed item acquired. On sub-
+ sequent runs, URLs that appear in the links file are
+ not duplicated as new directories.
+
+ -r Optionally, create barf or blagh directories
+ under root. Default is the current directory.
+
+ -t Create tag for each post (barf only).
+
+ -u The feed URL.
+
+ Trrss is a shell script that wraps the rrss program,
+ outputting plain text but preserving link URLs.
+
+ SOURCE
+ http://plan9.stanleylieber.com/src/rrss.tgz
+ SEE ALSO
+ http://werc.cat-v.org
+ http://werc.cat-v.org/apps/blagh
+ https://code.9front.org/hg/barf
--- /dev/null
+++ b/trrss
@@ -1,0 +1,8 @@
+#!/bin/rc
+# Run rrss and convert HTML to plain text, retaining link URLs.
+# NOTE: Requires plan9port or 9base. Fix the shebang path to rc.
+rrss $* | sed '
+ s/^title:.*$/<p>&/g
+ s/^link:.*$/<br>&/g
+ s/^date:.*$/<br>&<br>/g
+ ' | tcs -t html | htmlfmt -a -c utf-8 | uhtml