First commit
This commit is contained in:
parent
18106b32a7
commit
f1deabc799
|
@ -0,0 +1 @@
|
|||
openring
|
26
README.md
26
README.md
|
@ -1,3 +1,27 @@
|
|||
# openring-it
|
||||
|
||||
Italian adaptation of Drew Devault’s 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!
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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=
|
|
@ -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>
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue