all repos — flounder @ a297c298b1ae42c18be5d1092888c98b76410540

A small site builder for the Gemini protocol

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		time.Sleep(time.Hour * 1)
 26		users, err := getActiveUserNames()
 27		if err != nil {
 28			// Handle error somehow
 29			fmt.Println(err)
 30			continue
 31		}
 32		for _, user := range users {
 33			writeAllFeeds(user)
 34		}
 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	defer outputf.Close()
130	if err != nil {
131		return err
132	}
133	err = gt.ExecuteTemplate(outputf, "following.gmi", data)
134	if err != nil {
135		return err
136	}
137	// convert to gemini template
138	// write template to file
139	return nil
140}