all repos

rss-tools @ 71ed842

get rss feed from sources that(i need and) dont provide one

rss-tools/app/atom.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
update feed api again, 1 month ago
1
package app
2
3
import (
4
	"bytes"
5
	"crypto/sha1"
6
	"encoding/xml"
7
	"fmt"
8
	"io"
9
	"net/http"
10
	"time"
11
)
12
13
type AtomFeed struct {
14
	XMLName  xml.Name    `xml:"feed"`
15
	XMLNS    string      `xml:"xmlns,attr"`
16
	Title    string      `xml:"title"`
17
	ID       string      `xml:"id"`
18
	Updated  string      `xml:"updated"`
19
	Subtitle string      `xml:"subtitle,omitempty"`
20
	Entries  []AtomEntry `xml:"entry"`
21
}
22
23
type AtomEntry struct {
24
	Title   string      `xml:"title"`
25
	ID      string      `xml:"id"`
26
	Updated string      `xml:"updated"`
27
	Content AtomContent `xml:"content"`
28
}
29
30
type AtomContent struct {
31
	XMLName xml.Name `xml:"content"`
32
	Type    string   `xml:"type,attr,omitempty"`
33
	Value   string   `xml:",chardata"`
34
}
35
36
type FeedEntry struct {
37
	Title       string
38
	ID          string
39
	Content     string
40
	ContentType string // "text" or "html", defaults to "text"
41
	Updated     time.Time
42
}
43
44
type FeedBuilder struct{ f AtomFeed }
45
46
func NewFeed(title, id string) *FeedBuilder {
47
	return &FeedBuilder{f: AtomFeed{
48
		XMLNS:   "http://www.w3.org/2005/Atom",
49
		Title:   title,
50
		ID:      id,
51
		Updated: time.Now().Format(time.RFC3339),
52
	}}
53
}
54
55
func (f *FeedBuilder) WithSubtitle(subtitle string) *FeedBuilder {
56
	f.f.Subtitle = subtitle
57
	return f
58
}
59
60
func (f *FeedBuilder) WithUpdated(updated time.Time) *FeedBuilder {
61
	if !updated.IsZero() {
62
		f.f.Updated = updated.Format(time.RFC3339)
63
	}
64
	return f
65
}
66
67
func (f *FeedBuilder) Add(entry FeedEntry) *FeedBuilder {
68
	if entry.Updated.IsZero() {
69
		entry.Updated = time.Now()
70
	}
71
	if entry.ID == "" {
72
		hash := sha1.Sum(fmt.Appendf(nil, "%s|%s|%s", entry.Title, entry.Content, entry.Updated.Format(time.RFC3339Nano)))
73
		entry.ID = fmt.Sprintf("urn:sha1:%x", hash)
74
	}
75
76
	contentType := entry.ContentType
77
	if contentType == "" {
78
		contentType = "text"
79
	}
80
81
	f.f.Entries = append(f.f.Entries, AtomEntry{
82
		Title:   entry.Title,
83
		ID:      entry.ID,
84
		Updated: entry.Updated.Format(time.RFC3339),
85
		Content: AtomContent{
86
			Type:  contentType,
87
			Value: entry.Content,
88
		},
89
	})
90
91
	feedUpdated, err := time.Parse(time.RFC3339, f.f.Updated)
92
	if err != nil || entry.Updated.After(feedUpdated) {
93
		f.f.Updated = entry.Updated.Format(time.RFC3339)
94
	}
95
	return f
96
}
97
98
func (f *FeedBuilder) SetUpdated(updated time.Time) *FeedBuilder {
99
	if !updated.IsZero() {
100
		f.f.Updated = updated.Format(time.RFC3339)
101
	}
102
	return f
103
}
104
105
func (f *FeedBuilder) WriteTo(w io.Writer) error {
106
	enc := xml.NewEncoder(w)
107
	enc.Indent("", "  ")
108
	return enc.Encode(f.f)
109
}
110
111
func (f *FeedBuilder) Bytes() ([]byte, error) {
112
	var buf bytes.Buffer
113
	if err := f.WriteTo(&buf); err != nil {
114
		return nil, err
115
	}
116
	return buf.Bytes(), nil
117
}
118
119
func (f *FeedBuilder) Render(w http.ResponseWriter) error {
120
	w.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
121
	return f.WriteTo(w)
122
}