Skip to content

Commit

Permalink
feat: add miniflux/freshrss backends
Browse files Browse the repository at this point in the history
allows adding of remote backends for fetching feeds

closes #3
  • Loading branch information
guyfedwards authored Jan 29, 2023
1 parent dbefabe commit 4866ff2
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 14 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ feeds:
```
You can customise the location of the config file with the `--config-path` flag.

### Backends
As well as adding feeds directly, you can pull in feeds from another source. You can add multiple backends and the feeds will all be added.
```yaml
backends:
miniflux:
host: http://myminiflux.foo
api_key: jafksdljfladjfk
freshrss:
host: http://myfreshrss.bar
user: admin
password: muchstrong
```

## Usage
```sh
$ nom # open TUI
Expand All @@ -43,4 +56,12 @@ Running the nom via docker
```sh
docker run --rm -it nom
```
Use the `-v` command line argument to mount a local config onto `/app/docker-config.yml` as desired.
Use the `-v` command line argument to mount a local config onto `/app/docker-config.yml` as desired.


## Dev setup
You can use the `backends-compose.yml` to spin up a local instance of miniflux and freshrss if needed for development.

```sh
$ docker-compose -f backends-compose.yml up
```
42 changes: 42 additions & 0 deletions backends-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: '3.4'
services:
miniflux:
image: miniflux/miniflux:latest
ports:
- "8080:8080"
depends_on:
- db
environment:
- DATABASE_URL=postgres://miniflux:secret@db:5432/miniflux?sslmode=disable

db:
image: postgres:15
environment:
- POSTGRES_USER=miniflux
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s

freshrss:
image: freshrss/freshrss
container_name: freshrss
hostname: freshrss
restart: unless-stopped
environment:
TZ: Europe/Paris
CRON_MIN: '3,33'
FRESHRSS_ENV: development
ADMIN_EMAIL: [email protected]
ADMIN_PASSWORD: freshrss
ADMIN_API_PASSWORD: freshrss
ports:
- "8081:80"

volumes:
miniflux-db:
data:
extensions:
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ require (
github.com/charmbracelet/lipgloss v0.6.0
github.com/jessevdk/go-flags v1.4.0
github.com/mmcdole/gofeed v1.1.3
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/term v0.4.0
gopkg.in/yaml.v3 v3.0.1
miniflux.app v0.0.0-20230118040013-65febebd40b2
)

require (
Expand Down Expand Up @@ -40,9 +41,9 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/yuin/goldmark v1.4.14 // indirect
github.com/yuin/goldmark v1.5.3 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
)
17 changes: 12 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.14 h1:jwww1XQfhJN7Zm+/a1ZA/3WUiEBEroYFNTiV3dKwM8U=
github.com/yuin/goldmark v1.4.14/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand All @@ -120,8 +121,9 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -132,16 +134,19 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
Expand All @@ -153,3 +158,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
miniflux.app v0.0.0-20230118040013-65febebd40b2 h1:lInabmsUswyWat0M6RHORZLwyKepiVlSlZ0n/4b+sxo=
miniflux.app v0.0.0-20230118040013-65febebd40b2/go.mod h1:p4jt7G344RsXumFwatfjTdYwG750tmCVNklXpConCrM=
85 changes: 85 additions & 0 deletions internal/config/backends.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package config

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"

miniflux "miniflux.app/client"
)

func getMinifluxFeeds(config *MinifluxBackend) ([]Feed, error) {
mf := miniflux.New(config.Host, config.APIKey)

// Fetch all feeds.
feeds, err := mf.Feeds()
if err != nil {
return []Feed{}, err
}

var ret []Feed

for _, f := range feeds {
ret = append(ret, Feed{URL: f.FeedURL})
}

return ret, nil
}

type FreshRSSResponse struct {
Subscriptions []FreshRSSFeed `yaml:"subscriptions,omitempty"`
}

type FreshRSSFeed struct {
URL string `yaml:"url,omitempty"`
}

func getFreshRSSFeeds(config *FreshRSSBackend) ([]Feed, error) {
resp, err := http.Get(fmt.Sprintf("%v/api/greader.php/accounts/ClientLogin?Email=%v&Passwd=%v", config.Host, config.User, config.Password))
if err != nil {
return []Feed{}, err
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []Feed{}, err
}

// response is text containing the authorization pair
lines := strings.Split(string(body), "\n")
kv := strings.Split(lines[0], "=")

url := fmt.Sprintf("%v/api/greader.php/reader/api/0/subscription/list?output=json", config.Host)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return []Feed{}, err
}

req.Header.Add("Authorization", fmt.Sprintf("GoogleLogin auth=%v", kv[1]))

resp, err = http.DefaultClient.Do(req)
if err != nil {
return []Feed{}, err
}

body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return []Feed{}, err
}

var b FreshRSSResponse
err = json.Unmarshal(body, &b)
if err != nil {
return []Feed{}, err
}

var ret []Feed

for _, f := range b.Subscriptions {
ret = append(ret, Feed{URL: f.URL})
}

return ret, nil
}
44 changes: 41 additions & 3 deletions internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,30 @@ type Feed struct {
URL string `yaml:"url"`
}

type MinifluxBackend struct {
Host string `yaml:"host"`
APIKey string `yaml:"api_key"`
}

type FreshRSSBackend struct {
Host string `yaml:"host"`
User string `yaml:"user"`
Password string `yaml:"password"`
}

type Backends struct {
Miniflux *MinifluxBackend `yaml:"miniflux,omitempty"`
FreshRSS *FreshRSSBackend `yaml:"freshrss,omitempty"`
}

type Config struct {
configPath string
Pager string `yaml:"pager,omitempty"`
NoCache bool `yaml:"no-cache,omitempty"`
Feeds []Feed `yaml:"feeds"`
// Preview feeds are distinguished from Feeds because we don't want to inadvertenly write those into the config file.
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
PreviewFeeds []Feed `yaml:"previewfeeds,omitempty"`
Backends *Backends `yaml:"backends,omitempty"`
}

func New(configPath string, pager string, noCache bool, previewFeeds []string) (Config, error) {
Expand All @@ -34,8 +51,8 @@ func New(configPath string, pager string, noCache bool, previewFeeds []string) (
}

var f []Feed
for _, feedUrl := range previewFeeds {
f = append(f, Feed{feedUrl})
for _, feedURL := range previewFeeds {
f = append(f, Feed{feedURL})
}

return Config{
Expand Down Expand Up @@ -75,6 +92,26 @@ func (c *Config) Load() error {
c.Pager = fileConfig.Pager
}

if fileConfig.Backends != nil {
if fileConfig.Backends.Miniflux != nil {
mffeeds, err := getMinifluxFeeds(fileConfig.Backends.Miniflux)
if err != nil {
return err
}

c.Feeds = append(c.Feeds, mffeeds...)
}

if fileConfig.Backends.FreshRSS != nil {
freshfeeds, err := getFreshRSSFeeds(fileConfig.Backends.FreshRSS)
if err != nil {
return err
}

c.Feeds = append(c.Feeds, freshfeeds...)
}
}

return nil
}

Expand Down Expand Up @@ -119,6 +156,7 @@ func (c *Config) GetFeeds() []Feed {
if c.IsPreviewMode() {
return c.PreviewFeeds
}

return c.Feeds
}

Expand Down

0 comments on commit 4866ff2

Please sign in to comment.