Browse Source

change twi poarams, struct init for notify

Umputun 5 years ago
parent
commit
4f00d00cd6
3 changed files with 42 additions and 29 deletions
  1. 10 8
      app/main.go
  2. 30 19
      app/rss/notify.go
  3. 2 2
      app/rss/notify_test.go

+ 10 - 8
app/main.go

@@ -21,10 +21,10 @@ var opts struct {
 	TimeOut time.Duration `short:"t" long:"timeout" env:"TIMEOUT" default:"5s" description:"twitter timeout"`
 	Feed    string        `short:"f" long:"feed" env:"FEED" required:"true" description:"rss feed url"`
 
-	ConsumerKey    string `long:"consumer-key" env:"CONSUMER_KEY" required:"true" description:"twitter consumer key"`
-	ConsumerSecret string `long:"consumer-secret" env:"CONSUMER_SECRET" required:"true" description:"twitter consumer secret"`
-	AccessToken    string `long:"access-token" env:"ACCESS_TOKEN" required:"true" description:"twitter access token"`
-	AccessSecret   string `long:"access-secret" env:"ACCESS_SECRET" required:"true" description:"twitter access secret"`
+	ConsumerKey    string `long:"consumer-key" env:"TWI_CONSUMER_KEY" required:"true" description:"twitter consumer key"`
+	ConsumerSecret string `long:"consumer-secret" env:"TWI_CONSUMER_SECRET" required:"true" description:"twitter consumer secret"`
+	AccessToken    string `long:"access-token" env:"TWI_ACCESS_TOKEN" required:"true" description:"twitter access token"`
+	AccessSecret   string `long:"access-secret" env:"TWI_ACCESS_SECRET" required:"true" description:"twitter access secret"`
 
 	Template string `long:"template" env:"TEMPLATE" default:"{{.Title}} - {{.Link}}" description:"twitter message template"`
 	Dry      bool   `long:"dry" env:"DRY" description:"dry mode"`
@@ -34,26 +34,27 @@ var opts struct {
 var revision = "unknown"
 
 func main() {
-	fmt.Printf("RSS2TWITTER - %s\n", revision)
+	fmt.Printf("rss2twitter - %s\n", revision)
 	if _, err := flags.Parse(&opts); err != nil {
 		os.Exit(1)
 	}
 
 	setupLog(opts.Dbg)
 
-	notifier := rss.New(context.Background(), opts.Feed, opts.Refresh)
+	notifier := rss.Notify{Feed: opts.Feed, Duration: opts.Refresh, Timeout: opts.TimeOut}
 	var pub publisher.Interface = publisher.Twitter{
 		ConsumerKey:    opts.ConsumerKey,
 		ConsumerSecret: opts.ConsumerSecret,
 		AccessToken:    opts.AccessToken,
 		AccessSecret:   opts.AccessSecret,
 	}
-	if opts.Dry {
+
+	if opts.Dry { // override publisher to stdout only, no actual twitter publishing
 		pub = publisher.Stdout{}
 		log.Print("[INFO] dry mode")
 	}
 
-	for event := range notifier.Go() {
+	for event := range notifier.Go(context.Background()) {
 		err := pub.Publish(event, func(r rss.Event) string {
 			b1 := bytes.Buffer{}
 			if err := template.Must(template.New("twi").Parse(opts.Template)).Execute(&b1, event); err != nil {
@@ -65,6 +66,7 @@ func main() {
 			log.Printf("[WARN] failed to publish, %s", err)
 		}
 	}
+	log.Print("[INFO] terminated")
 }
 
 func setupLog(dbg bool) {

+ 30 - 19
app/rss/notify.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"log"
 	"net/http"
+	"sync"
 	"time"
 
 	"github.com/mmcdole/gofeed"
@@ -11,9 +12,11 @@ import (
 
 // Notify on RSS change
 type Notify struct {
-	feed     string
-	duration time.Duration
+	Feed     string
+	Duration time.Duration
+	Timeout  time.Duration
 
+	once   sync.Once
 	ctx    context.Context
 	cancel context.CancelFunc
 }
@@ -26,44 +29,52 @@ type Event struct {
 	guid      string
 }
 
-// New makes notifier for given rss feed. Checks for new items every duration
-func New(ctx context.Context, feed string, duration time.Duration) *Notify {
-	res := Notify{feed: feed, duration: duration}
-	res.ctx, res.cancel = context.WithCancel(ctx)
-	log.Printf("[INFO] crate notifier for %q, %s", feed, duration)
-	return &res
-}
-
 // Go starts notifier and returns events channel
-func (n *Notify) Go() <-chan Event {
+func (n *Notify) Go(ctx context.Context) <-chan Event {
+	log.Printf("[INFO] start notifier for %s, every %s", n.Feed, n.Duration)
+	n.once.Do(func() { n.ctx, n.cancel = context.WithCancel(ctx) })
+
 	ch := make(chan Event)
+
+	// wait for duration, can be terminated by ctx
+	waitOrCancel := func(ctx context.Context) bool {
+		select {
+		case <-ctx.Done():
+			return false
+		case <-time.After(n.Duration):
+			return true
+		}
+	}
+
 	go func() {
+
 		defer func() {
 			close(ch)
 			n.cancel()
 		}()
+
 		fp := gofeed.NewParser()
-		fp.Client = &http.Client{Timeout: time.Second * 5}
+		fp.Client = &http.Client{Timeout: n.Timeout}
 		lastGUID := ""
 		for {
-			feedData, err := fp.ParseURL(n.feed)
+			feedData, err := fp.ParseURL(n.Feed)
 			if err != nil {
-				log.Printf("[WARN] failed to fetch from %s, %s", n.feed, err)
-				time.Sleep(n.duration)
+				log.Printf("[WARN] failed to fetch from %s, %s", n.Feed, err)
+				if !waitOrCancel(n.ctx) {
+					return
+				}
 				continue
 			}
 			event := n.feedEvent(feedData)
 			if lastGUID != event.guid {
-				if lastGUID != "" {
+				if lastGUID != "" { // don't notify on initial change
 					log.Printf("[DEBUG] new event %s", event.guid)
 					ch <- event
 				}
 				lastGUID = event.guid
 			}
-			select {
-			case <-n.ctx.Done():
+			if !waitOrCancel(n.ctx) {
 				return
-			case <-time.After(n.duration):
 			}
 		}
 	}()

+ 2 - 2
app/rss/notify_test.go

@@ -28,8 +28,8 @@ func TestNotify(t *testing.T) {
 	}))
 
 	defer ts.Close()
-	notify := New(context.Background(), ts.URL, time.Millisecond*250)
-	ch := notify.Go()
+	notify := Notify{Feed: ts.URL, Duration: time.Millisecond * 250, Timeout: time.Millisecond * 100}
+	ch := notify.Go(context.Background())
 	defer notify.Shutdown()
 
 	st := time.Now()