openring/openring.go

150 lines
2.9 KiB
Go
Raw Normal View History

2019-06-03 18:27:44 +00:00
package main
import (
"html"
2019-06-03 18:27:44 +00:00
"html/template"
"io/ioutil"
"log"
"net/url"
"os"
"sort"
"strconv"
"time"
"github.com/SlyMarbo/rss"
"github.com/mattn/go-runewidth"
"github.com/microcosm-cc/bluemonday"
"git.sr.ht/~sircmpwn/getopt"
)
type Article struct {
Date time.Time
Link string
SourceLink string
SourceTitle string
Summary template.HTML
Title string
}
func main() {
var (
narticles int = 3
perSource int = 1
summaryLen int = 256
2019-06-03 18:27:44 +00:00
sources []*url.URL
)
opts, optind, err := getopt.Getopts(os.Args[1:], "l:n:p:s:")
2019-06-03 18:27:44 +00:00
if err != nil {
panic(err)
}
for _, opt := range opts {
switch opt.Option {
case 'l':
summaryLen, err = strconv.Atoi(opt.Value)
if err != nil {
panic(err)
}
case 'n':
narticles, err = strconv.Atoi(opt.Value)
if err != nil {
panic(err)
}
case 'p':
perSource, err = strconv.Atoi(opt.Value)
if err != nil {
panic(err)
}
2019-06-03 18:27:44 +00:00
case 's':
u, err := url.Parse(opt.Value)
if err != nil {
panic(err)
}
sources = append(sources, u)
}
}
if len(os.Args[optind+1:]) != 0 {
log.Fatalf(
"Usage: %s [-s https://source.rss...] < in.html > out.html",
os.Args[0])
}
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
tmpl, err := template.
New("template").
Funcs(map[string]interface{}{
"date": func(t time.Time) string {
return t.Format("January 2, 2006")
},
}).
Parse(string(input))
if err != nil {
panic(err)
}
log.Println("Fetching feeds...")
var feeds []*rss.Feed
for _, source := range sources {
feed, err := rss.Fetch(source.String())
if err != nil {
log.Printf("Error fetching %s: %s", source.String(), err.Error())
continue
}
feeds = append(feeds, feed)
log.Printf("Fetched %s", feed.Title)
}
if len(feeds) == 0 {
log.Fatal("Expected at least one feed to successfully fetch")
}
policy := bluemonday.StrictPolicy()
var articles []*Article
for _, feed := range feeds {
if len(feed.Items) == 0 {
log.Printf("Warning: feed %s has no items", feed.Title)
continue
}
items := feed.Items
if len(items) > perSource {
items = items[:perSource]
}
for _, item := range items {
raw_summary := item.Summary
if len(raw_summary) == 0 {
raw_summary = html.UnescapeString(item.Content)
}
summary := runewidth.Truncate(
policy.Sanitize(raw_summary), summaryLen, "…")
articles = append(articles, &Article{
Date: item.Date,
SourceLink: feed.Link,
SourceTitle: feed.Title,
Summary: template.HTML(summary),
Title: item.Title,
Link: item.Link,
})
}
2019-06-03 18:27:44 +00:00
}
sort.Slice(articles, func(i, j int) bool {
return articles[i].Date.After(articles[j].Date)
})
if len(articles) < narticles {
narticles = len(articles)
}
2019-06-03 18:27:44 +00:00
articles = articles[:narticles]
err = tmpl.Execute(os.Stdout, struct{
Articles []*Article
}{
Articles: articles,
})
if err != nil {
panic(err)
}
}