all repos — jrss @ 0eba4b0e156f91f6cce866062dbe7831a12d3aca

abandoned rss reader

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"

	"github.com/SlyMarbo/rss"
)

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([]rss.Feed, 0)
	var feed *rss.Feed
	fetchFailures := make([]error, 0)

	var wg sync.WaitGroup
	var m sync.Mutex
	wg.Add(len(scrapedFeeds))
	for _, f := range scrapedFeeds {
		go func(f string) {
			defer wg.Done()
			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()
				fetchFailures = append(fetchFailures, err)
				return
			}
			feeds = append(feeds, *feed)
		}(f)
	}
	wg.Wait()

	if len(fetchFailures) > 0 {
		for _, f := range fetchFailures {
			// put this on the website eventually??
			log.Printf("failed to fetch %s", f)
		}
	}

	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>")
		}
	}

	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) (*rss.Feed, error) {
	feed, err := rss.Fetch(url)
	if err != nil {
		return nil, err
	}
	return feed, nil
}

func makeFeedList(url string) ([]string, error) {
	res, err := http.Get(url)
	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
	}
	r := csv.NewReader(strings.NewReader(string(body)))
	feeds := make([]string, 0)
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			err = fmt.Errorf("makeFeedList record parsing error: %w\nvalue: %s", err, record)
			return nil, err
		}
		// [0] == url to a feed (item 0 of the csv)
		feeds = append(feeds, record[0])
	}
	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))
}