feed.go (view raw)
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "git.sr.ht/~adnano/go-gemini"
7 "github.com/mmcdole/gofeed"
8 "log"
9 "net/http"
10 "net/url"
11 "os"
12 "path"
13 "sort"
14 "time"
15)
16
17const followingPath = "following.txt"
18const followingFile = "following.gmi"
19
20// TODO also get gemini, gemfeed
21
22func feedsWorker() {
23 log.Println("Starting feeds worker")
24 for {
25 users, err := getActiveUserNames()
26 if err != nil {
27 // Handle error somehow
28 fmt.Println(err)
29 continue
30 }
31 for _, user := range users {
32 writeAllFeeds(user)
33 }
34 time.Sleep(time.Hour * 1)
35 }
36}
37
38func writeAllFeeds(user string) error {
39 // Open file
40 file, err := os.Open(path.Join(getUserDirectory(user), followingPath))
41 log.Println("Writing feeds for user " + user)
42 defer file.Close()
43
44 feedData := []*gofeed.Feed{}
45 if err == nil {
46 scanner := bufio.NewScanner(file)
47 count := 1
48 for scanner.Scan() {
49 if count > 100 { // max number of lines
50 break
51 }
52 count = count + 1
53 feedURL := scanner.Text()
54 parsed, err := url.Parse(feedURL)
55 var feed *gofeed.Feed
56 fp := gofeed.NewParser()
57 if err != nil {
58 log.Println("Invalid url " + feedURL)
59 }
60 if parsed.Scheme == "gemini" {
61 client := gemini.Client{
62 Timeout: 10 * time.Second,
63 }
64 res, err := client.Get(feedURL)
65 defer res.Body.Close()
66 if err != nil {
67 log.Println(err)
68 continue
69 }
70 if err != nil {
71 log.Println(err)
72 continue
73 }
74 feed, err = fp.Parse(res.Body)
75 if err != nil {
76 log.Println(err)
77 continue
78 }
79 } else {
80 // TODO if scheme is gemini and filetype is gemini... gemtext
81 // TODO rate limit etc
82 fp.Client = &http.Client{
83 Timeout: 10 * time.Second,
84 }
85 feed, err = fp.ParseURL(feedURL)
86 if err != nil {
87 log.Println("Error getting feed " + feedURL)
88 continue
89 }
90 }
91 log.Println("Got feed data from " + feedURL)
92 feedData = append(feedData, feed)
93 }
94 if err := scanner.Err(); err != nil {
95 return err
96 }
97 }
98 // Aggregate and sort by date
99 type feedPlusItem struct {
100 Feed *gofeed.Feed
101 FeedItem *gofeed.Item
102 Date string
103 }
104 data := struct {
105 User string
106 FeedItems []feedPlusItem
107 }{}
108 data.User = user
109 for _, feed := range feedData {
110 for _, item := range feed.Items {
111 if item.UpdatedParsed == nil {
112 item.UpdatedParsed = item.PublishedParsed
113 }
114 if item.UpdatedParsed != nil {
115 date := item.UpdatedParsed.Format("2006-01-02")
116 data.FeedItems = append(data.FeedItems, feedPlusItem{feed, item, date})
117 }
118 }
119 }
120 sort.Slice(data.FeedItems, func(i, j int) bool {
121 return data.FeedItems[i].FeedItem.UpdatedParsed.After(*data.FeedItems[j].FeedItem.UpdatedParsed)
122 })
123 maxItems := 100
124 if len(data.FeedItems) > maxItems {
125 data.FeedItems = data.FeedItems[:maxItems]
126 }
127
128 outputf, err := os.OpenFile(path.Join(getUserDirectory(user), followingFile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
129 if err != nil {
130 return err
131 }
132 err = gt.ExecuteTemplate(outputf, "following.gmi", data)
133 if err != nil {
134 return err
135 }
136 defer outputf.Close()
137 // convert to gemini template
138 // write template to file
139 return nil
140}