small pixel drawing of a pufferfish jrss

Remove tons of deps, simplify logic, use html/template
Jesse Olson j3s@c3f.net
Tue, 17 May 2022 03:31:36 +0000
commit

0eba4b0e156f91f6cce866062dbe7831a12d3aca

parent

6387c3457a0b2544f34da3dcd2d6630e47b5dc85

3 files changed, 83 insertions(+), 120 deletions(-)

jump to
M go.modgo.mod

@@ -1,14 +1,5 @@

-module giit.cyberia.club/~j3s/jrss +module git.j3s.sh/jrss go 1.13 -require ( - github.com/PuerkitoBio/goquery v1.6.1 // indirect - github.com/andybalholm/cascadia v1.2.0 // indirect - github.com/mmcdole/gofeed v1.1.0 - github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect - golang.org/x/text v0.3.5 // indirect -) +require github.com/SlyMarbo/rss v1.0.3
M go.sumgo.sum

@@ -1,54 +1,4 @@

-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk= -github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/mmcdole/gofeed v1.1.0 h1:T2WrGLVJRV04PY2qwhEJLHCt9JiCtBhb6SmC8ZvJH08= -github.com/mmcdole/gofeed v1.1.0/go.mod h1:PPiVwgDXLlz2N83KB4TrIim2lyYM5Zn7ZWH9Pi4oHUk= -github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI= -github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8= -github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 h1:Z6i7ND25ixRtXFBylIUggqpvLMV1I15yprcqMVB7WZA= -github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +github.com/SlyMarbo/rss v1.0.3 h1:AnP63RxQxa0flEaiOryiTe5J5es0KCCfji6lU+rQ8tc= +github.com/SlyMarbo/rss v1.0.3/go.mod h1:w6Bhn1BZs91q4OlEnJVZEUNRJmlbFmV7BkAlgCN8ofM= +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=
M main.gomain.go

@@ -11,25 +11,29 @@ "os"

"sort" "strings" "sync" + "text/template" + "time" - "github.com/mmcdole/gofeed" + "github.com/SlyMarbo/rss" ) -// There are two layers that we're dealing with here - -// The "feed" layer and the "post" layer. -// The biggest difference between the two is that -// the feed layer contains much more information about the -// "broader site" like the site title - while the post layer -// contains a bunch of post-related info. +type FeedItem struct { + FeedTitle string + PostTitle string + Link string + Date time.Time +} + func rootHandler(w http.ResponseWriter, r *http.Request) { + // replace this with hardcoded list from j3s.sh scrapedFeeds, err := makeFeedList(os.Args[1]) if err != nil { fmt.Fprintf(w, "makeList error: %s", err) log.Fatal(err) } - feeds := make([]gofeed.Feed, 0) - var feed *gofeed.Feed + feeds := make([]rss.Feed, 0) + var feed *rss.Feed fetchFailures := make([]error, 0) var wg sync.WaitGroup

@@ -42,6 +46,7 @@ feed, err = getFeed(f)

if err != nil { // break loop in case of // feed fetching failure + log.Print(err) err = fmt.Errorf("%s<br>%s", f, err) m.Lock() defer m.Unlock()

@@ -52,61 +57,73 @@ feeds = append(feeds, *feed)

}(f) } wg.Wait() - consolidatedFeed := consolidateFeeds(feeds) - // sort posts by date, most recent first - sort.Sort(sort.Reverse(consolidatedFeed)) - - // here we goooOooOoOoOooOooOo - // ooOooOOoooOOooooOOOOOooooOO - fmt.Fprintf(w, "<!doctype html>"+ - "<html lang=\"en\">"+ - "<meta charset=\"utf-8\" />"+ - "<title>jrss</title>"+ - "<table>"+ - "<tbody>") if len(fetchFailures) > 0 { for _, f := range fetchFailures { - fmt.Fprintf(w, "<p style=\"color:red\">failed to fetch %s</p>", f) + // put this on the website eventually?? + log.Printf("failed to fetch %s", f) } } - for _, post := range consolidatedFeed.Items { - // TODO: Add author name (nil pointer dereference) - fmt.Fprintf(w, "<tr>") - fmt.Fprintf(w, "<td>%s: (%s)<a href=\"%s\">%s</a> <small>%s</small></td>", - post.Custom["feed-title"], post.Author.Name, post.Link, post.Title, post.Published) - fmt.Fprintf(w, "</tr>") - } - fmt.Fprintf(w, "</tbody>"+"</table>"+"</html>") -} -// consolidateFeeds takes in a bunch of disparate feeds & mushes them into one -// big single omega-feed so that we may loop over it and sort it how we wish -func consolidateFeeds(inputFeeds []gofeed.Feed) gofeed.Feed { - omegaFeed := gofeed.Feed{} - // We stick all of the posts into one .Items - // So that they may be easily sorted (implemented in - // gofeeds) - for _, feed := range inputFeeds { - for _, post := range feed.Items { - // Pass a few feed-level vars down - // to the post level so that we may - // display them when we loop over the - // posts. - post.Custom = make(map[string]string) - post.Custom["feed-title"] = feed.Title - if post.Author == nil { - post.Author = &gofeed.Person{} - } - omegaFeed.Items = append(omegaFeed.Items, post) + var feedItems []FeedItem + for _, f := range feeds { + for _, i := range f.Items { + var fi FeedItem + fi.FeedTitle = f.Title + fi.PostTitle = i.Title + fi.Link = i.Link + fi.Date = i.Date + feedItems = append(feedItems, fi) + // fmt.Fprintf(w, "<tr>") + // fmt.Fprintf(w, "<td>%s: <a href=\"%s\">%s</a> <small>%s</small></td>", + // f.Title, i.Link, i.Title, i.Date) + // fmt.Fprintf(w, "</tr>") } } - return omegaFeed + + sort.Slice(feedItems, func(i, j int) bool { + return feedItems[i].Date.After(feedItems[j].Date) + }) + + // here we goooOooOoOoOooOooOo + // ooOooOOoooOOooooOOOOOooooOO + const tpl = ` + <!DOCTYPE html> + <html> + <head> + <meta charset="UTF-8"> + <title>jes's personal feed list</title> + </head> + <body> + <table><tbody> + {{range .}} + <tr> + <td> + {{.FeedTitle}}: <a href="{{.Link}}">{{.PostTitle}}</a> <small>{{.Date}}</small> + </td> + </tr> + {{end}} + </tbody></table> + </body> + </html>` + + t, err := template.New("jrss").Parse(tpl) + if err != nil { + log.Fatal(err) + } + + err = t.Execute(w, feedItems) + if err != nil { + log.Print(err) + } } -func getFeed(url string) (*gofeed.Feed, error) { - p := gofeed.NewParser() - return p.ParseURL(url) +func getFeed(url string) (*rss.Feed, error) { + feed, err := rss.Fetch(url) + if err != nil { + return nil, err + } + return feed, nil } func makeFeedList(url string) ([]string, error) {

@@ -115,12 +132,12 @@ if err != nil {

err = fmt.Errorf("makeFeedList http.Get: %w", err) return nil, err } + defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { err = fmt.Errorf("makeFeedList failed to read http body: %w", err) return nil, err } - res.Body.Close() r := csv.NewReader(strings.NewReader(string(body))) feeds := make([]string, 0) for {

@@ -138,11 +155,16 @@ }

return feeds, err } +func faviconHandler(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) +} + func main() { if len(os.Args) == 1 { log.Fatal("Supply your feed list as ARG1.\nExample: go run main.go https://j3s.sh/feeds.txt") } + http.HandleFunc("/favicon.ico", faviconHandler) http.HandleFunc("/", rootHandler) log.Fatal(http.ListenAndServe(":8080", nil)) }