rss/atom.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
package rss import ( "bytes" "encoding/xml" "fmt" "time" ) func parseAtom(data []byte) (*Feed, error) { warnings := false feed := atomFeed{} p := xml.NewDecoder(bytes.NewReader(data)) p.Strict = false p.CharsetReader = charsetReader err := p.Decode(&feed) if err != nil { return nil, err } out := new(Feed) out.Title = feed.Title out.Description = feed.Description for _, link := range feed.Link { if link.Rel == "alternate" || link.Rel == "" { out.Link = link.Href break } } out.Image = feed.Image.Image() out.Refresh = time.Now().Add(DefaultRefreshInterval) out.Items = make([]*Item, 0, len(feed.Items)) out.ItemMap = make(map[string]struct{}) // Process items. for _, item := range feed.Items { // Skip items already known. if _, ok := out.ItemMap[item.ID]; ok { continue } next := new(Item) next.Title = item.Title next.Summary = item.Summary if item.Date != "" { next.Date, err = parseTime(item.Date) if err == nil { next.DateValid = true } } next.ID = item.ID for _, link := range item.Links { if link.Rel == "alternate" || link.Rel == "" { next.Link = link.Href } else { next.Enclosures = append(next.Enclosures, &Enclosure{ URL: link.Href, Type: link.Type, Length: link.Length, }) } } next.Read = false if next.ID == "" { if debug { fmt.Printf("[w] Item %q has no ID and will be ignored.\n", next.Title) fmt.Printf("[w] %#v\n", item) } warnings = true continue } if _, ok := out.ItemMap[next.ID]; ok { if debug { fmt.Printf("[w] Item %q has duplicate ID.\n", next.Title) fmt.Printf("[w] %#v\n", next) } warnings = true continue } out.Items = append(out.Items, next) out.ItemMap[next.ID] = struct{}{} out.Unread++ } if warnings && debug { fmt.Printf("[i] Encountered warnings:\n%s\n", data) } return out, nil } type atomFeed struct { XMLName xml.Name `xml:"feed"` Title string `xml:"title"` Description string `xml:"subtitle"` Link []atomLink `xml:"link"` Image atomImage `xml:"image"` Items []atomItem `xml:"entry"` Updated string `xml:"updated"` } type atomItem struct { XMLName xml.Name `xml:"entry"` Title string `xml:"title"` Summary string `xml:"summary"` Links []atomLink `xml:"link"` Date string `xml:"updated"` DateValid bool ID string `xml:"id"` } type atomImage struct { XMLName xml.Name `xml:"image"` Title string `xml:"title"` URL string `xml:"url"` Height int `xml:"height"` Width int `xml:"width"` } type atomLink struct { Href string `xml:"href,attr"` Rel string `xml:"rel,attr"` Type string `xml:"type,attr"` Length uint `xml:"length,attr"` } func (a *atomImage) Image() *Image { out := new(Image) out.Title = a.Title out.URL = a.URL out.Height = uint32(a.Height) out.Width = uint32(a.Width) return out }