commit 89681da2caf53b6354c4997d9a74431805a039ef Author: Dave Gallant Date: Fri Feb 28 23:09:43 2020 -0500 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..579f710 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# srv + +View RSS feeds from the terminal. + +![image](https://user-images.githubusercontent.com/4519234/77839285-49077c00-7149-11ea-80ad-76efda38615e.png) + +## configure + +srv reads configuration from `~/.config/srv/config.yaml` + +If a configuration is not provided, a default configuration is generated. + +- `feeds` is a list of RSS/Atom feeds to be loaded in srv. +- `externalViewer` defines an application to override the default web browser (optional). + +An example config can be copied: + +```shell +cp ./config-example.yaml ~/.config/srv/config.yaml +``` + +## control + +Key mappings are statically defined for the time being. + +- `TAB` switches between Feeds and Items. +- `UP/DOWN` navigates feeds and items` +- `ENTER` either selects a feed or opens a feed item in an external application. +- `F5` refresh list of feeds + +## build + +```shell +make build +``` + +## test + +```shell +make test +``` diff --git a/config.json b/config.json new file mode 100644 index 0000000..9fa7382 --- /dev/null +++ b/config.json @@ -0,0 +1,9 @@ +{ + "feeds": [ + "https://news.ycombinator.com/rss", + "https://www.reddit.com/r/golang/.rss", + "https://www.reddit.com/r/linux/.rss", + "https://www.zdnet.com/topic/security/rss.xml", + "https://aws.amazon.com/blogs/security/feed/" + ] +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9197535 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/davegallant/rssgo + +go 1.14 + +require ( + github.com/PuerkitoBio/goquery v1.5.1 // indirect + github.com/jroimartin/gocui v0.4.0 + github.com/mattn/go-runewidth v0.0.8 // indirect + github.com/mmcdole/gofeed v1.0.0-beta2 + github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf // indirect + github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be // indirect + github.com/stretchr/testify v1.5.1 // indirect + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect + golang.org/x/text v0.3.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0edef84 --- /dev/null +++ b/go.sum @@ -0,0 +1,35 @@ +github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= +github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= +github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mmcdole/gofeed v1.0.0-beta2 h1:CjQ0ADhAwNSb08zknAkGOEYqr8zfZKfrzgk9BxpWP2E= +github.com/mmcdole/gofeed v1.0.0-beta2/go.mod h1:/BF9JneEL2/flujm8XHoxUcghdTV6vvb3xx/vKyChFU= +github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI= +github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8= +github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be h1:yzmWtPyxEUIKdZg4RcPq64MfS8NA6A5fNOJgYhpR9EQ= +github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..c93df0c --- /dev/null +++ b/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + + "github.com/jroimartin/gocui" + "github.com/mmcdole/gofeed" +) + +var Configuration struct { + Feeds []string +} + +var filename = flag.String("config", "config.json", "Location of the config file.") + +func main() { + flag.Parse() + data, err := ioutil.ReadFile(*filename) + if err != nil { + log.Panicln(err) + } + if err != nil { + log.Panicln(err) + } + + err = json.Unmarshal(data, &Configuration) + if err != nil { + log.Panicln(err) + } + + g, err := gocui.NewGui(gocui.OutputNormal) + if err != nil { + log.Panicln(err) + } + defer g.Close() + + g.SetManagerFunc(layout) + + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + log.Panicln(err) + } +} + +func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + if v, err := g.SetView("feeds", maxX/2-26, maxY/5, maxX/2+8, maxY/2+6); err != nil { + if err != gocui.ErrUnknownView { + return err + } + fp := gofeed.NewParser() + for _, f := range Configuration.Feeds { + feed, err := fp.ParseURL(f) + if err != nil { + fmt.Println(err) + } + if feed != nil { + fmt.Fprintln(v, feed.Title) + } + } + } + return nil +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +}