ref: 0237ae12582923c2d34bbd866a339540909a995c
dir: /main.go/
// RSS feed reader that outputs plain text, werc/apps/barf package main import ( "bufio" "flag" "fmt" "html" "io/ioutil" "log" "net/http" "os" "path" "sort" "strconv" "strings" "time" "github.com/SlyMarbo/rss" ) type Article struct { Title string Link string Date time.Time Content string Tags []string } type renderer func(articles []Article) type filter func(article *Article) var ( debug = flag.Bool("d", false, "print debug msgs to stderr") format = flag.String("f", "", "output format") root = flag.String("r", "", "output root") dest string links string ) var filters = map[string]filter { "http://feeds.feedburner.com/Explosm": filterexplosm, } func usage() { os.Stderr.WriteString("usage: rrss [-d] [-f barf|blagh] [-r root] <feed file>\n") flag.PrintDefaults() os.Exit(2) } func check(err error) { if err != nil { log.Fatal(err) } } func fetchfeed(url string) (resp *http.Response, err error) { client := http.DefaultClient req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Add("User-Agent", "Mozilla/5.0 (compatible; hjdicks)") return client.Do(req) } func isold(date time.Time, link string, path string) bool { file, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0775) if err != nil { return true } defer file.Close() s := fmt.Sprintf("%d_%s", date.Unix(), link); scanner := bufio.NewScanner(file) for scanner.Scan() { if strings.Contains(s, scanner.Text()) { return true } } return false } func makeold(date time.Time, link string, path string) (int, error) { f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0775) defer f.Close() check(err) if link == "" { link = "empty" } s := fmt.Sprintf("%d_%s", date.Unix(), link); return f.WriteString(s + "\n") } func writef(dir, filename, content string) { err := ioutil.WriteFile(path.Join(dir, filename), []byte(content+"\n"), 0775) check(err) } func ensuredir(dir string) { err := os.MkdirAll(dir, 0775) check(err) } // http://code.9front.org/hg/barf func barf(articles []Article) { dest = path.Join(*root, "src") ensuredir(dest) n := lastarticle(dest) for _, a := range articles { n = n + 1 d := fmt.Sprintf("%s/%d", dest, n) ensuredir(d) writef(d, "title", a.Title) writef(d, "link", a.Link) writef(d, "date", a.Date.String()) writef(d, "body", a.Content) if a.Tags != nil { ensuredir(path.Join(d, "tags")) for _, j := range a.Tags { f, err := os.Create(d + "/tags/" + j) f.Close() check(err) } } _, err := makeold(a.Date, a.Link, links) check(err) } } // http://werc.cat-v.org/apps/blagh func blagh(articles []Article) { var err error for _, a := range articles { dest = path.Join(*root, fmt.Sprintf("%d/%02d/%02d", a.Date.Year(), a.Date.Month(), a.Date.Day())); ensuredir(dest) f, _ := os.Open(dest) // directory will usually not exist yet defer f.Close() n, _ := f.Readdirnames(0) d := fmt.Sprintf("%s/%d", dest, len(n)) ensuredir(d) writef(d, "index.md", fmt.Sprintf("%s\n===\n\n%s\n", a.Title, a.Content)) _, err = makeold(a.Date, a.Link, links) check(err) } } func stdout(articles []Article) { for _, a := range articles { fmt.Printf("title: %s\nlink: %s\ndate: %s\n%s\n\n", a.Title, a.Link, a.Date, a.Content) } } func lastarticle(dir string) int { f, err := os.Open(dir) defer f.Close() check(err) dn, err := f.Readdirnames(0) check(err) var di []int for _, j := range dn { k, _ := strconv.Atoi(j) di = append(di, k) } sort.Ints(di) n := 0 if di != nil { n = di[len(di)-1] } return n } func filterexplosm(article *Article) { r, err := http.Get(article.Link) if err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) } defer r.Body.Close() scanner := bufio.NewScanner(r.Body) for scanner.Scan() { if strings.Contains(scanner.Text(), "main-comic") { s := strings.Replace(scanner.Text(), "src=\"", "src=\"http:", 1) article.Content = s break; } } } func loadfeed(url string, tags []string) []Article { var articles []Article if *debug { log.Printf("Fetching feed '%s'", url) } feed, err := rss.FetchByFunc(fetchfeed, url) if err != nil { log.Printf("Cannot load feed '%s': %v", url, err) return nil } for _, i := range feed.Items { if isold(i.Date, i.Link, links) { continue } a := Article{i.Title, i.Link, i.Date, conorsum(i), tags} if f, ok := filters[url]; ok { f(&a) } articles = append(articles, a) } if *debug { log.Printf("Loaded %d items", len(articles)) } return articles } func conorsum(i *rss.Item) string { s := "" switch{ case len(i.Content) > 0: s = i.Content case len(i.Summary) > 0: s = i.Summary } return html.UnescapeString(s) } func main() { flag.Usage = usage flag.Parse() if flag.Arg(0) == "" { usage() } var render renderer switch *format { case "barf": render = barf case "blagh": render = blagh case "": render = stdout default: usage() } links = path.Join(*root, "links") file, err := os.Open(flag.Arg(0)) check(err) defer file.Close() scanner := bufio.NewScanner(file) var articles []Article var tags []string for scanner.Scan() { t := scanner.Text() if len(t) <= 0 { continue } l := strings.Split(t, " ") if len(l) > 1 { tags = l[1:] } a := loadfeed(l[0], tags) if a != nil { articles = append(articles, a...) } } sort.Slice(articles, func(i, j int) bool { return articles[i].Date.Before(articles[j].Date) }) render(articles) }