From a846cb2a424fd17d43e3edf885cca7d79820fa9f Mon Sep 17 00:00:00 2001
From: Ruben Pollan <meskio@sindominio.net>
Date: Thu, 8 Feb 2018 21:41:31 +0100
Subject: [PATCH] [feat] use a config file to check when to produce the next
 notification

---
 bitmask/darwin.go  |  2 +-
 bitmask/events.go  |  2 +-
 bitmask/unix.go    |  2 +-
 bitmask/windows.go |  2 +-
 config.go          | 74 ++++++++++++++++++++++++++++++++++++++++++++++
 main.go            | 10 +++++--
 notificator.go     | 15 ++++++----
 systray.go         | 28 +++++++++++++++---
 8 files changed, 120 insertions(+), 15 deletions(-)
 create mode 100644 config.go

diff --git a/bitmask/darwin.go b/bitmask/darwin.go
index f3e92247..84934360 100644
--- a/bitmask/darwin.go
+++ b/bitmask/darwin.go
@@ -4,4 +4,4 @@ package bitmask
 
 import "os"
 
-var configPath = os.Getenv("HOME") + "/Library/Preferences/leap"
+var ConfigPath = os.Getenv("HOME") + "/Library/Preferences/leap"
diff --git a/bitmask/events.go b/bitmask/events.go
index 8645519b..5808d51b 100644
--- a/bitmask/events.go
+++ b/bitmask/events.go
@@ -72,5 +72,5 @@ func (b *Bitmask) eventsHandler() {
 }
 
 func getServerKeyPath() string {
-	return filepath.Join(configPath, "events", "zmq_certificates", "public_keys", "server.key")
+	return filepath.Join(ConfigPath, "events", "zmq_certificates", "public_keys", "server.key")
 }
diff --git a/bitmask/unix.go b/bitmask/unix.go
index 5b50f24c..5bb522c1 100644
--- a/bitmask/unix.go
+++ b/bitmask/unix.go
@@ -4,4 +4,4 @@ package bitmask
 
 import "os"
 
-var configPath = os.Getenv("HOME") + "/.config/leap"
+var ConfigPath = os.Getenv("HOME") + "/.config/leap"
diff --git a/bitmask/windows.go b/bitmask/windows.go
index a574c966..36481f5d 100644
--- a/bitmask/windows.go
+++ b/bitmask/windows.go
@@ -4,4 +4,4 @@ package bitmask
 
 import "os"
 
-var configPath = os.Getenv("APPDATA") + "\\leap"
+var ConfigPath = os.Getenv("APPDATA") + "\\leap"
diff --git a/config.go b/config.go
new file mode 100644
index 00000000..0a769aa1
--- /dev/null
+++ b/config.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"os"
+	"path"
+	"time"
+
+	"0xacab.org/leap/bitmask-systray/bitmask"
+)
+
+const (
+	oneDay   = time.Hour * 24
+	oneMonth = oneDay * 30
+)
+
+var (
+	configPath = path.Join(bitmask.ConfigPath, "systray.json")
+)
+
+type systrayConfig struct {
+	LastNotification time.Time
+	Donated          time.Time
+}
+
+func parseConfig() (*systrayConfig, error) {
+	var conf systrayConfig
+
+	f, err := os.Open(configPath)
+	if os.IsNotExist(err) {
+		return &conf, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	dec := json.NewDecoder(f)
+	err = dec.Decode(&conf)
+	return &conf, err
+}
+
+func (c *systrayConfig) hasDonated() bool {
+	log.Println("has donated ", c.Donated.Add(oneMonth))
+	return c.Donated.Add(oneMonth).After(time.Now())
+}
+
+func (c *systrayConfig) needsNotification() bool {
+	log.Println("needs ", c.LastNotification.Add(oneDay))
+	log.Println(!c.hasDonated() && c.LastNotification.Add(oneDay).Before(time.Now()))
+	return !c.hasDonated() && c.LastNotification.Add(oneDay).Before(time.Now())
+}
+
+func (c *systrayConfig) setNotification() error {
+	c.LastNotification = time.Now()
+	return c.save()
+}
+
+func (c *systrayConfig) setDonated() error {
+	c.Donated = time.Now()
+	return c.save()
+}
+
+func (c *systrayConfig) save() error {
+	f, err := os.Create(configPath)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	enc := json.NewEncoder(f)
+	return enc.Encode(c)
+}
diff --git a/main.go b/main.go
index ce06c97c..76343ebb 100644
--- a/main.go
+++ b/main.go
@@ -11,12 +11,18 @@ const (
 )
 
 func main() {
-	go notificate()
+	// TODO: do I need to bootstrap the provider?
+	conf, err := parseConfig()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	go notificate(conf)
 
 	b, err := bitmask.Init()
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	run(b)
+	run(b, conf)
 }
diff --git a/notificator.go b/notificator.go
index 1e602218..2a46b001 100644
--- a/notificator.go
+++ b/notificator.go
@@ -8,18 +8,23 @@ import (
 	"github.com/0xAX/notificator"
 )
 
-const notificationText = `The RiseupVPN service is expensive to run. Because we don't want to store personal information about you, there is no accounts or billing for this service. But if you want the service to continue, donate at least $5 each month at https://riseup.net/donate-vpn`
+const (
+	notificationText = `The RiseupVPN service is expensive to run. Because we don't want to store personal information about you, there is no accounts or billing for this service. But if you want the service to continue, donate at least $5 each month at https://riseup.net/donate-vpn`
+)
 
-func notificate() {
+func notificate(conf *systrayConfig) {
 	wd, _ := os.Getwd()
 	notify := notificator.New(notificator.Options{
 		DefaultIcon: path.Join(wd, "riseupvpn.svg"),
 		AppName:     "RiseupVPN",
 	})
 
+	time.Sleep(time.Minute * 5)
 	for {
-		time.Sleep(time.Minute * 5)
-		notify.Push("Donate to RiseupVPN", notificationText, "", notificator.UR_NORMAL)
-		time.Sleep(time.Hour * 24)
+		if conf.needsNotification() {
+			notify.Push("Donate to RiseupVPN", notificationText, "", notificator.UR_NORMAL)
+			conf.setNotification()
+		}
+		time.Sleep(time.Hour)
 	}
 }
diff --git a/systray.go b/systray.go
index 3da4d19a..4344748e 100644
--- a/systray.go
+++ b/systray.go
@@ -13,10 +13,13 @@ import (
 
 type bmTray struct {
 	bm            *bitmask.Bitmask
+	conf          *systrayConfig
 	waitCh        chan bool
 	mStatus       *systray.MenuItem
 	mTurnOn       *systray.MenuItem
 	mTurnOff      *systray.MenuItem
+	mDonate       *systray.MenuItem
+	mHaveDonated  *systray.MenuItem
 	mCancel       *systray.MenuItem
 	activeGateway *gatewayTray
 }
@@ -26,8 +29,8 @@ type gatewayTray struct {
 	name     string
 }
 
-func run(bm *bitmask.Bitmask) {
-	bt := bmTray{bm: bm}
+func run(bm *bitmask.Bitmask, conf *systrayConfig) {
+	bt := bmTray{bm: bm, conf: conf}
 	systray.Run(bt.onReady, bt.onExit)
 }
 
@@ -50,7 +53,8 @@ func (bt *bmTray) onReady() {
 	bt.addGateways()
 
 	mHelp := systray.AddMenuItem("Help ...", "")
-	mDonate := systray.AddMenuItem("Donate ...", "")
+	bt.mDonate = systray.AddMenuItem("Donate ...", "")
+	bt.mHaveDonated = systray.AddMenuItem("... I have donated", "")
 	mAbout := systray.AddMenuItem("About ...", "")
 	systray.AddSeparator()
 
@@ -66,6 +70,8 @@ func (bt *bmTray) onReady() {
 		}
 
 		for {
+			bt.updateDonateMenu()
+
 			select {
 			case status := <-ch:
 				log.Println("status: " + status)
@@ -83,8 +89,10 @@ func (bt *bmTray) onReady() {
 
 			case <-mHelp.ClickedCh:
 				open.Run("https://riseup.net/vpn")
-			case <-mDonate.ClickedCh:
+			case <-bt.mDonate.ClickedCh:
 				open.Run("https://riseup.net/donate-vpn")
+			case <-bt.mHaveDonated.ClickedCh:
+				bt.conf.setDonated()
 			case <-mAbout.ClickedCh:
 				open.Run("https://bitmask.net")
 
@@ -93,6 +101,8 @@ func (bt *bmTray) onReady() {
 				bt.bm.Close()
 				log.Println("Quit now...")
 				os.Exit(0)
+
+			case <-time.After(time.Minute * 30):
 			}
 		}
 	}()
@@ -192,6 +202,16 @@ func (bt *bmTray) changeStatus(status string) {
 	bt.mStatus.SetTooltip("RiseupVPN is " + statusStr)
 }
 
+func (bt *bmTray) updateDonateMenu() {
+	if bt.conf.hasDonated() {
+		go bt.mHaveDonated.Hide()
+		go bt.mDonate.Hide()
+	} else {
+		go bt.mHaveDonated.Show()
+		go bt.mDonate.Show()
+	}
+}
+
 func (bt *bmTray) waitIcon() {
 	icons := [][]byte{icon.Wait0, icon.Wait1, icon.Wait2, icon.Wait3}
 	for i := 0; true; i = (i + 1) % 4 {
-- 
GitLab