forked from SlyMarbo/rss
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First attempt at Feed.Update (experimental!)
- Loading branch information
Showing
1 changed file
with
98 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,133 @@ | ||
package rss | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"strings" | ||
"time" | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// Parse RSS or Atom data. | ||
func Parse(data []byte) (*Feed, error) { | ||
if strings.Contains(string(data), "<rss") { | ||
return parseRSS2(data, database) | ||
} else if strings.Contains(string(data), "xmlns=\"http://purl.org/rss/1.0/\"") { | ||
return parseRSS1(data, database) | ||
} else { | ||
return parseAtom(data, database) | ||
} | ||
|
||
panic("Unreachable.") | ||
if strings.Contains(string(data), "<rss") { | ||
return parseRSS2(data, database) | ||
} else if strings.Contains(string(data), "xmlns=\"http://purl.org/rss/1.0/\"") { | ||
return parseRSS1(data, database) | ||
} else { | ||
return parseAtom(data, database) | ||
} | ||
|
||
panic("Unreachable.") | ||
} | ||
|
||
// Fetch downloads and parses the RSS feed at the given URL | ||
func Fetch(url string) (*Feed, error) { | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
|
||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return Parse(body) | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
|
||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return Parse(body) | ||
} | ||
|
||
// Feed is the top-level structure. | ||
type Feed struct { | ||
Title string | ||
Description string | ||
Link string | ||
Image *Image | ||
Items []*Item | ||
Refresh time.Time | ||
Unread uint32 | ||
Title string | ||
Description string | ||
Link string | ||
Image *Image | ||
Items []*Item | ||
Refresh time.Time | ||
Unread uint32 | ||
} | ||
|
||
// Update fetches any new items and updates f. | ||
func (f *Feed) Update() error { | ||
|
||
// Check that we don't update too often. | ||
if f.Refresh.After(time.Now()) { | ||
return nil | ||
} | ||
|
||
if f.Link == "" { | ||
return errors.New("Error: feed has no URL.") | ||
} | ||
|
||
update, err := Fetch(f.Link) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
f.Refresh = update.Refresh | ||
f.Title = update.Title | ||
f.Description = update.Description | ||
|
||
// Find the offset between items. | ||
offset := 0 | ||
for _, item := range f.Items { | ||
if item.ID == update.Items[0].ID { | ||
break | ||
} | ||
offset++ | ||
} | ||
|
||
for i, item := range update.Items { | ||
if i+offset >= len(f.Items) { | ||
f.Items = append(f.Items, item) | ||
f.Unread++ | ||
} else if f.Items[i+offset].ID != item.ID { | ||
return errors.New("Error: offsets don't match.") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (f *Feed) String() string { | ||
buf := new(bytes.Buffer) | ||
buf.WriteString(fmt.Sprintf("Feed %q\n\t%q\n\t%q\n\t%s\n\tRefresh at %s\n\tUnread: %d\n\tItems:\n", | ||
f.Title, f.Description, f.Link, f.Image, f.Refresh.Format("Mon 2 Jan 2006 15:04:05 MST"), f.Unread)) | ||
for _, item := range f.Items { | ||
buf.WriteString(fmt.Sprintf("\t%s\n", item.Format("\t\t"))) | ||
} | ||
return buf.String() | ||
buf := new(bytes.Buffer) | ||
buf.WriteString(fmt.Sprintf("Feed %q\n\t%q\n\t%q\n\t%s\n\tRefresh at %s\n\tUnread: %d\n\tItems:\n", | ||
f.Title, f.Description, f.Link, f.Image, f.Refresh.Format("Mon 2 Jan 2006 15:04:05 MST"), f.Unread)) | ||
for _, item := range f.Items { | ||
buf.WriteString(fmt.Sprintf("\t%s\n", item.Format("\t\t"))) | ||
} | ||
return buf.String() | ||
} | ||
|
||
// Item represents a single story. | ||
type Item struct { | ||
Title string | ||
Content string | ||
Link string | ||
Date time.Time | ||
ID string | ||
Read bool | ||
Title string | ||
Content string | ||
Link string | ||
Date time.Time | ||
ID string | ||
Read bool | ||
} | ||
|
||
func (i *Item) String() string { | ||
return i.Format("") | ||
return i.Format("") | ||
} | ||
|
||
func (i *Item) Format(s string) string { | ||
return fmt.Sprintf("Item %q\n\t%s%q\n\t%s%s\n\t%s%q\n\t%sRead: %v\n\t%s%q", i.Title, s, i.Link, s, | ||
i.Date.Format("Mon 2 Jan 2006 15:04:05 MST"), s, i.ID, s, i.Read, s, i.Content) | ||
return fmt.Sprintf("Item %q\n\t%s%q\n\t%s%s\n\t%s%q\n\t%sRead: %v\n\t%s%q", i.Title, s, i.Link, s, | ||
i.Date.Format("Mon 2 Jan 2006 15:04:05 MST"), s, i.ID, s, i.Read, s, i.Content) | ||
} | ||
|
||
type Image struct { | ||
Title string | ||
Url string | ||
Height uint32 | ||
Width uint32 | ||
Title string | ||
Url string | ||
Height uint32 | ||
Width uint32 | ||
} | ||
|
||
func (i *Image) String() string { | ||
return fmt.Sprintf("Image %q", i.Title) | ||
return fmt.Sprintf("Image %q", i.Title) | ||
} |