@@ 7,7 7,7 @@ import (
"github.com/BurntSushi/toml"
)
-type config struct {
+type Config struct {
Server string `toml:"server"`
Username string `toml:"username"`
Password string `toml:"password"`
@@ 17,8 17,8 @@ type config struct {
WebhookURL string `toml:"webhook_url"`
}
-func loadConfig(configPath string) (*config, error) {
- var c config
+func LoadConfig(configPath string) (*Config, error) {
+ var c Config
if _, err := toml.DecodeFile(configPath, &c); err != nil {
return nil, err
@@ 31,7 31,7 @@ func loadConfig(configPath string) (*config, error) {
return &c, nil
}
-func (c *config) validate() error {
+func (c *Config) validate() error {
for k, v := range map[string]string{
"server url": c.Server,
"username": c.Username,
@@ 1,17 1,12 @@
package main
import (
- "bytes"
- "encoding/json"
"fmt"
- "html/template"
"io/ioutil"
"log"
- "net/http"
"os"
"strconv"
"strings"
- "time"
"git.sr.ht/~sircmpwn/getopt"
@@ 19,7 14,11 @@ import (
)
var version = "0.1.0"
-var webhookTemplate *template.Template
+
+type Mfn struct {
+ conf *Config
+ notifiers map[string]Notifier
+}
func loadLastEntry(filepath string) (int64, error) {
bytes, err := ioutil.ReadFile(filepath)
@@ 44,55 43,13 @@ func saveLastEntry(filepath string, id int64) error {
return ioutil.WriteFile(filepath, []byte(strconv.FormatInt(id, 10)), 0644)
}
-func sendWebhook(entry *mf.Entry, webhookURL string) error {
- var text bytes.Buffer
- if err := webhookTemplate.Execute(&text, entry); err != nil {
- return err
- }
-
- body := map[string]string{"text": text.String(), "format": "html", "displayName": "Miniflux", "avatarUrl": "https://miniflux.app/favicon.ico"}
- jsonVal, err := json.Marshal(body)
- if err != nil {
- return err
- }
-
- res, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonVal))
- if err != nil {
- return err
- }
- resBytes, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return nil
- }
- log.Printf("Webhook response: %s", string(resBytes))
-
- return nil
-}
-
-func testWebhook(webhookURL string) error {
- e := &mf.Entry{
- ID: 0,
- UserID: 0,
- FeedID: 0,
- Status: "unread",
- Hash: "deadbeef",
- Title: "Test Entry",
- URL: "https://miniflux.app",
- Date: time.Now(),
- Content: "This is a test miniflux entry",
- Author: "Author",
- Starred: false,
- Enclosures: make([]*mf.Enclosure, 0),
- Feed: nil,
- }
-
- return sendWebhook(e, webhookURL)
-}
-
func main() {
showHelp := getopt.Bool("h", false, "show usage")
- webhookTest := getopt.Bool("t", false, "test the webhook endpoint")
showVersion := getopt.Bool("v", false, "show version")
+
+ var testNotifier string
+ getopt.StringVar(&testNotifier, "t", "", "test a notifier")
+
var configPath string
getopt.StringVar(&configPath, "c", "", "configuration file path")
@@ 102,7 59,7 @@ func main() {
}
if *showHelp {
- fmt.Println("Usage: mfn [-v] [-t] -c config")
+ fmt.Println("Usage: mfn [-v] [-t endpoint] -c config")
os.Exit(0)
}
@@ 115,29 72,41 @@ func main() {
log.Fatal("You must specify a config location")
}
- conf, err := loadConfig(configPath)
+ conf, err := LoadConfig(configPath)
if err != nil {
log.Fatalf("Failed to load config: %s\n", err)
}
- webhookTemplate = conf.WebhookTemplate
- if *webhookTest {
- log.Println("Sending test webhook...")
- if err := testWebhook(conf.WebhookURL); err != nil {
- log.Fatalf("Test webhook failed: %s\n", err)
+ notifiers := make(map[string]Notifier)
+ notifiers["webhook"] = NewWebhook(conf)
+
+ mfn := &Mfn{
+ conf: conf,
+ notifiers: notifiers,
+ }
+
+ if testNotifier != "" {
+ notifier, ok := mfn.notifiers[testNotifier]
+ if !ok {
+ log.Fatalf("Invalid notifier: %s\n", testNotifier)
+ }
+
+ log.Println("Sending test notification...")
+ if err := notifier.Test(); err != nil {
+ log.Fatalf("Test notification failed: %s\n", err)
}
os.Exit(0)
}
- if err := execNotifications(conf); err != nil {
+ if err := mfn.execNotifications(); err != nil {
log.Fatal(err)
}
}
-func execNotifications(conf *config) error {
- client := mf.New(conf.Server, conf.Username, conf.Password)
+func (mfn *Mfn) execNotifications() error {
+ client := mf.New(mfn.conf.Server, mfn.conf.Username, mfn.conf.Password)
- lastEntry, err := loadLastEntry(conf.DbPath)
+ lastEntry, err := loadLastEntry(mfn.conf.DbPath)
if err != nil {
return fmt.Errorf("failed to load last entry: %s", err)
}
@@ 178,12 147,14 @@ func execNotifications(conf *config) error {
continue
}
- if err := sendWebhook(e, conf.WebhookURL); err != nil {
- log.Printf("Failed to send webhook: %s\n", err)
+ for name, notifier := range mfn.notifiers {
+ if err := notifier.Notify(e); err != nil {
+ log.Printf("Failed to send %s notification: %s\n", name, err)
+ }
}
}
log.Printf("Saving new last entry: %d\n", lastEntry)
- if err := saveLastEntry(conf.DbPath, lastEntry); err != nil {
+ if err := saveLastEntry(mfn.conf.DbPath, lastEntry); err != nil {
return fmt.Errorf("failed to save new entries: %s", err)
}
@@ 0,0 1,71 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "html/template"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "time"
+
+ mf "miniflux.app/client"
+)
+
+type Webhook struct {
+ // Notifier
+ template *template.Template
+ url string
+}
+
+func NewWebhook(conf *Config) *Webhook {
+ return &Webhook{
+ template: conf.WebhookTemplate,
+ url: conf.WebhookURL,
+ }
+}
+
+func (w *Webhook) Notify(entry *mf.Entry) error {
+ var text bytes.Buffer
+ if err := w.template.Execute(&text, entry); err != nil {
+ return err
+ }
+
+ body := map[string]string{"text": text.String(), "format": "html", "displayName": "Miniflux", "avatarUrl": "https://miniflux.app/favicon.ico"}
+ jsonVal, err := json.Marshal(body)
+ if err != nil {
+ return err
+ }
+
+ res, err := http.Post(w.url, "application/json", bytes.NewBuffer(jsonVal))
+ if err != nil {
+ return err
+ }
+ resBytes, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil
+ }
+ log.Printf("Webhook response: %s", string(resBytes))
+
+ return nil
+}
+
+func (w *Webhook) Test() error {
+ e := &mf.Entry{
+ ID: 0,
+ UserID: 0,
+ FeedID: 0,
+ Status: "unread",
+ Hash: "deadbeef",
+ Title: "Test Entry",
+ URL: "https://miniflux.app",
+ Date: time.Now(),
+ Content: "This is a test miniflux entry",
+ Author: "Author",
+ Starred: false,
+ Enclosures: make([]*mf.Enclosure, 0),
+ Feed: nil,
+ }
+
+ return w.Notify(e)
+}