diff --git a/.gitignore b/.gitignore index eb30223..1527fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ settings.json golang_news +!plugins/* +*.so diff --git a/main.go b/main.go index a1db4a9..04184bc 100644 --- a/main.go +++ b/main.go @@ -1,82 +1,80 @@ package main import ( + "fmt" "log" "os" - "regexp" + "plugin" + "reflect" "github.com/SlyMarbo/rss" ) +type Feed struct { + URL string + ItemHandler func(*rss.Item) string +} + func main() { log.SetOutput(os.Stdout) log.Println("Starting") + + feeds, err := feeds() + if err != nil { + return + } tweets := make(chan string) - go pollFeeds(tweets) + go pollFeeds(tweets, feeds) postTweets(tweets) } -func pollFeeds(publishTweet chan string) { - // variable number of select/case: https://play.golang.org/p/8zwvSk4kjx - blogItems := make(chan *rss.Item) - hnItems := make(chan *rss.Item) - redditItems := make(chan *rss.Item) +func feeds() ([]Feed, error) { + p, err := plugin.Open(os.Args[1]) + if err != nil { + log.Printf("Error reading plugin: %s", err) + return nil, err + } + listFunc, err := p.Lookup("List") + if err != nil { + log.Printf("Error looking up 'List': %s", err) + return nil, err + } - go poller("https://blog.golang.org/feed.atom", blogItems) - go poller("https://news.ycombinator.com/rss", hnItems) - go poller("https://www.reddit.com/r/golang.rss", redditItems) + return listFunc.(func() []Feed)(), nil +} - for { - select { - case item := <-blogItems: - publishTweet <- blogItem(item) - case item := <-hnItems: - publishTweet <- hnItem(item) - case item := <-redditItems: - publishTweet <- redditItem(item) - } +func pollFeeds(publishTweet chan string, feeds []Feed) { + itemProducers := []chan *rss.Item{} + for _, feed := range feeds { + itemProducer := make(chan *rss.Item) + itemProducers = append(itemProducers, itemProducer) + go poller(feed.URL, itemProducer) } -} -func postTweets(tweets chan string) { - for tweet := range tweets { - if tweet != "" { - PostTweet(tweet) - } + cases := make([]reflect.SelectCase, len(itemProducers)) + for i, ch := range itemProducers { + cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)} } -} -func hnItem(item *rss.Item) string { - if match, _ := regexp.MatchString(`\w Go( |$|\.)`, item.Title); match { - short_title := item.Title - if len(short_title) > 100 { - short_title = short_title[:99] + "…" + activeItemProducers := len(cases) + for activeItemProducers > 0 { + chosen, value, ok := reflect.Select(cases) + if !ok { + // The chosen channel has been closed, so zero out the channel to disable the case + cases[chosen].Chan = reflect.ValueOf(nil) + activeItemProducers -= 1 + continue } - return short_title + " " + item.Link + " #hackernews" - } else { - log.Printf("Ignoring Hackernews item: %s", item.Title) - return "" - } -} -func blogItem(item *rss.Item) string { - short_title := item.Title - if len(short_title) > 100 { - short_title = short_title[:99] + "…" + fmt.Printf("Read from channel %#v and received %s\n", itemProducers[chosen], value.String()) } - return short_title + " " + "https:" + item.Link + " #go_blog" } -func redditItem(item *rss.Item) string { - re := regexp.MustCompile(`([^"]+)">\[link\]`) - matches := re.FindStringSubmatch(item.Content) - if len(matches) == 2 { - short_title := item.Title - if len(short_title) > 100 { - short_title = short_title[:99] + "…" +func postTweets(tweets chan string) { + for tweet := range tweets { + if tweet != "" { + PostTweet(tweet) } - return short_title + " " + matches[1] + " #reddit" } - return "" } diff --git a/plugins/golang_news/main.go b/plugins/golang_news/main.go new file mode 100644 index 0000000..c6d0934 --- /dev/null +++ b/plugins/golang_news/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "log" + "regexp" + + "C" + + "github.com/SlyMarbo/rss" +) + +type Feed struct { + URL string + ItemHandler func(*rss.Item) string +} + +func List() []Feed { + return []Feed{ + Feed{ + URL: "https://blog.golang.org/feed.atom", + ItemHandler: blogItem, + }, + Feed{ + URL: "https://news.ycombinator.com/rss", + ItemHandler: hnItem, + }, + Feed{ + URL: "https://www.reddit.com/r/golang.rss", + ItemHandler: redditItem, + }, + } +} + +func hnItem(item *rss.Item) string { + if match, _ := regexp.MatchString(`\w Go( |$|\.)`, item.Title); match { + short_title := item.Title + if len(short_title) > 100 { + short_title = short_title[:99] + "…" + } + return short_title + " " + item.Link + " #hackernews" + } else { + log.Printf("Ignoring Hackernews item: %s", item.Title) + return "" + } +} + +func blogItem(item *rss.Item) string { + short_title := item.Title + if len(short_title) > 100 { + short_title = short_title[:99] + "…" + } + return short_title + " " + "https:" + item.Link + " #go_blog" +} + +func redditItem(item *rss.Item) string { + re := regexp.MustCompile(`([^"]+)">\[link\]`) + matches := re.FindStringSubmatch(item.Content) + if len(matches) == 2 { + short_title := item.Title + if len(short_title) > 100 { + short_title = short_title[:99] + "…" + } + return short_title + " " + matches[1] + " #reddit" + } + return "" +}