First commit

This commit is contained in:
zinricky 2022-09-08 09:52:40 +02:00
parent 18106b32a7
commit f1deabc799
6 changed files with 280 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
openring

View File

@ -1,3 +1,27 @@
# openring-it
Italian adaptation of Drew Devaults openring
This is a tool for generating a webring from RSS feeds, so you can link to
other blogs you like on your own blog. It's designed to be fairly simple and
integrate with any static site generator. The basic usage is:
```
openring \
-s https://drewdevault.com/feed.xml \
-s https://emersion.fr/blog/rss.xml \
-s https://danluu.com/atom.xml \
< in.html \
> out.html
```
This will read the template at in.html (an example is provided, but feel free to
adjust it to suit your needs), fetch the latest 5 articles from among your
sources, and pass them to the template and write the output to out.html. Then
you can include this file with your static site generator's normal file include
mechanism.
## Contributing
Want to help? [Send patches](https://git-send-email.io) to the original openring's [mailing
list](https://lists.sr.ht/~sircmpwn/public-inbox):
[`~sircmpwn/public-inbox@lists.sr.ht`](mailto:~sircmpwn/public-inbox@lists.sr.ht).
Thanks!

10
go.mod Normal file
View File

@ -0,0 +1,10 @@
module git.sr.ht/~sircmpwn/openring
go 1.12
require (
git.sr.ht/~sircmpwn/getopt v0.0.0-20190621174457-292febf82fd0
github.com/SlyMarbo/rss v1.0.1
github.com/mattn/go-runewidth v0.0.4
github.com/microcosm-cc/bluemonday v1.0.2
)

18
go.sum Normal file
View File

@ -0,0 +1,18 @@
git.sr.ht/~sircmpwn/getopt v0.0.0-20190214165041-9a4f886f9fc7 h1:xTFH5S/3ltiRvAtETLLDFWm5nVIouT5GeCPHm8UaVEU=
git.sr.ht/~sircmpwn/getopt v0.0.0-20190214165041-9a4f886f9fc7/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
git.sr.ht/~sircmpwn/getopt v0.0.0-20190621174457-292febf82fd0 h1:gUeOEsT0mhoCKxKYJk8HeYtUZME686xs70eG2l80W5U=
git.sr.ht/~sircmpwn/getopt v0.0.0-20190621174457-292febf82fd0/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
github.com/SlyMarbo/rss v1.0.1 h1:fiaIU5UhcXauVOniHOIocWG7uj8Ej6pHNarMGPJilzA=
github.com/SlyMarbo/rss v1.0.1/go.mod h1:JNF+T33oj4m5WLCQXpBTCgO+SxRbYVgdiiimHNgzcbA=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

21
in.html Normal file
View File

@ -0,0 +1,21 @@
<section class="openring">
<h3>Articoli da altri blog</h3>
<section class="openarticles">
{{range .Articles}}
<div class="openarticle">
<h4 class="opentitle">
<a href="{{.Link}}">{{.Title}}</a>
</h4>
<p class="opensummary">{{.Summary}}</p>
<small class="opensource">
da <a href="{{.SourceLink}}">{{.SourceTitle}}</a>
</small>
<small class="opendate">{{.Date | datef "2 January 2006"}}</small>
</div>
{{end}}
</section>
<p class="openattribution">
Generato con
<a href="https://git.sr.ht/~sircmpwn/openring">openring</a>
</p>
</section>

205
openring.go Normal file
View File

@ -0,0 +1,205 @@
// License-Id: GPL-3.0-only
// Copyright: 2019 Drew DeVault <sir@cmpwn.com>
// Copyright: 2019 Haelwenn (lanodan) Monnier <contact@hacktivis.me>
// Copyright: 2019 Jeff Kaufman <jeff.t.kaufman@gmail.com>
// Copyright: 2019 Nate Dobbins <nated@posteo.net>
// Copyright: 2019 Noah Loomans <noah@noahloomans.com>
// Copyright: 2019 Philip K <philip@warpmail.net>
// Copyright: 2019 Simon Ser <contact@emersion.fr>
// Copyright: 2020 Drew DeVault <sir@cmpwn.com>
// Copyright: 2020 skuzzymiglet <skuzzymiglet@gmail.com>
// Copyright: 2021 Gianluca Arbezzano <ciao@gianarb.it>
// Copyright: 2021 sourque <contact@sourque.com>
package main
import (
"bufio"
"html"
"html/template"
"io/ioutil"
"log"
"net/url"
"os"
"sort"
"strings"
"time"
"git.sr.ht/~sircmpwn/getopt"
"github.com/SlyMarbo/rss"
"github.com/mattn/go-runewidth"
"github.com/microcosm-cc/bluemonday"
)
type urlSlice []*url.URL
func (us *urlSlice) String() string {
var str []string
for _, u := range *us {
str = append(str, u.String())
}
return strings.Join(str, ", ")
}
func (us *urlSlice) Set(val string) error {
u, err := url.Parse(val)
if err != nil {
return err
}
*us = append(*us, u)
return nil
}
type Article struct {
Date time.Time
Link string
SourceLink string
SourceTitle string
Summary template.HTML
Title string
}
func main() {
var (
narticles = getopt.Int("n", 5, "article count")
perSource = getopt.Int("p", 1, "articles to take from each source")
summaryLen = getopt.Int("l", 256, "length of summaries")
urlsFile = getopt.String("S", "", "file with URLs of sources")
sources []*url.URL
)
getopt.Var((*urlSlice)(&sources), "s", "list of sources")
getopt.Usage = func() {
log.Fatalf("Usage: %s [-s https://source.rss...] < in.html > out.html",
os.Args[0])
}
err := getopt.Parse()
if err != nil {
panic(err)
}
if *urlsFile != "" {
file, err := os.Open(*urlsFile)
if err != nil {
panic(err)
}
sc := bufio.NewScanner(file)
for sc.Scan() {
(*urlSlice)(&sources).Set(sc.Text())
}
file.Close()
}
input, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
rimpiazzo := strings.NewReplacer(
"January", "gennaio",
"February", "febbraio",
"March", "marzo",
"April", "aprile",
"May", "maggio",
"June", "giugno",
"July", "luglio",
"August", "agosto",
"September", "settembre",
"October", "ottobre",
"November", "novembre",
"December", "dicembre", )
tmpl, err := template.
New("template").
Funcs(map[string]interface{}{
"date": func(t time.Time) string {
return t.Format("January 2, 2006")
},
"datef": func(fmt string, t time.Time) string {
return rimpiazzo.Replace(t.Format(fmt))
},
}).
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
}
if feed.Title == "" {
log.Printf("Warning: feed from %s has no title", source.Host)
feed.Title = source.Host
}
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]
}
base, err := url.Parse(feed.UpdateURL)
if err != nil {
log.Fatal("failed parsing update URL of the feed")
}
feedLink, err := url.Parse(feed.Link)
if err != nil {
log.Fatal("failed parsing canonical feed URL of the feed")
}
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, "…")
itemLink, err := url.Parse(item.Link)
if err != nil {
log.Fatal("failed parsing article URL of the feed item")
}
articles = append(articles, &Article{
Date: item.Date,
SourceLink: base.ResolveReference(feedLink).String(),
SourceTitle: feed.Title,
Summary: template.HTML(summary),
Title: item.Title,
Link: base.ResolveReference(itemLink).String(),
})
}
}
sort.Slice(articles, func(i, j int) bool {
return articles[i].Date.After(articles[j].Date)
})
if len(articles) < *narticles {
*narticles = len(articles)
}
articles = articles[:*narticles]
err = tmpl.Execute(os.Stdout, struct {
Articles []*Article
}{
Articles: articles,
})
if err != nil {
panic(err)
}
}