diff --git a/mods/main.go b/mods/main.go
index dfbb389..fd5f996 100644
--- a/mods/main.go
+++ b/mods/main.go
@@ -52,6 +52,10 @@ var mods = map[string]Mod{
OnInit: initstt,
OnMessage: stt,
},
+ "remind": {
+ OnInit: initremind,
+ OnMessage: remind,
+ },
}
func initmods() {
@@ -102,6 +106,7 @@ func main() {
gillmt = flag.String("gillmt", "gill.ttf", "Path to gill.ttf (Gill Sans MT font)")
sourcesans = flag.String("sourcesans", "source.ttf", "Path to source.ttf (Source Sans Pro font)")
macropath = flag.String("macropath", "macros.json", "Path to macros db (JSON)")
+ remindpath = flag.String("remindpath", "reminders.json", "Path to reminder db (JSON)")
proverbi = flag.String("proverbi", "proverbi.txt", "Path to proverbi pairs (separated by /)")
wittoken = flag.String("wit", "", "Wit.ai token")
gapifile = flag.String("gapi", "gapi.json", "Google API Service Credentials file")
@@ -127,10 +132,11 @@ func main() {
initmods()
var err error
- broker, err = tg.CreateBrokerClient(*brokerAddr, dispatch)
- if err != nil {
- panic(err)
- }
+ broker, err = tg.ConnectToBroker(*brokerAddr)
+ assert(err)
+ defer broker.Close()
+
+ assert(tg.RunBrokerClient(broker, dispatch))
}
func assert(err error) {
diff --git a/mods/remind.go b/mods/remind.go
new file mode 100644
index 0000000..a1c6377
--- /dev/null
+++ b/mods/remind.go
@@ -0,0 +1,175 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+
+ "github.com/hamcha/clessy/tg"
+)
+
+var remindpath *string
+
+type Reminder struct {
+ TargetID int64
+ When int64
+ Text string
+}
+
+var reminders map[string]Reminder
+
+const ReminderMaxDuration = time.Hour * 24 * 30 * 3
+
+func initremind() {
+ reminders = make(map[string]Reminder)
+ file, err := os.Open(*remindpath)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+ err = json.NewDecoder(file).Decode(&reminders)
+ if err != nil {
+ log.Println("[remind] WARN: Could not load pending reminders (malformed or unreadable file): " + err.Error())
+ return
+ }
+ for id, _ := range reminders {
+ go runreminder(id)
+ }
+ log.Printf("[remind] Loaded %d pending reminders from %s\n", len(reminders), *remindpath)
+}
+
+func remind(broker *tg.Broker, update tg.APIMessage) {
+ if isCommand(update, "ricordami") {
+ // Supported formats:
+ // Xs/m/h/d => in X seconds/minutes/hours/days
+ // HH:MM => at HH:MM (24 hour format)
+ // HH:MM:SS => at HH:MM:SS (24 hour format)
+ // dd/mm/yyyy => same hour, specific date
+ // dd/mm/yyyy-HH:MM => specific hour, specific dat
+ // dd/mm/yyyy-HH:MM:SS => specific hour, specific date
+
+ parts := strings.SplitN(*(update.Text), " ", 3)
+ if len(parts) < 3 {
+ broker.SendTextMessage(update.Chat, "Sintassi\n/ricordami [quando] Messaggio\n\nFormati supportati per [quando]:\n 10s 10m 10h 10d (secondi/minuti/ore/giorni)\n 13:20 15:55:01 (ora dello stesso giorno, formato 24h)\n 11/02/2099 11/02/2099-11:20:01 (giorno diverso, stessa ora [1] o specifica [2])", &update.MessageID)
+ return
+ }
+
+ format := parts[1]
+ message := parts[2]
+
+ timestamp, err := parseDuration(format)
+ if err != nil {
+ broker.SendTextMessage(update.Chat, err.Error(), &update.MessageID)
+ return
+ }
+
+ id := strconv.FormatInt(update.Chat.ChatID, 36) + "-" + strconv.FormatInt(update.MessageID, 36)
+ reminders[id] = Reminder{
+ TargetID: update.User.UserID,
+ When: timestamp.Unix(),
+ Text: message,
+ }
+ savereminder()
+ go runreminder(id)
+
+ whenday := "più tardi"
+ _, todaym, todayd := time.Now().Date()
+ _, targetm, targetd := timestamp.Date()
+ if todaym != targetm || todayd != targetd {
+ whenday = "il " + timestamp.Format("2/1")
+ }
+ whentime := "alle " + timestamp.Format("15:04:05")
+ broker.SendTextMessage(update.Chat, "Ok, vedrò di avvisarti "+whenday+" "+whentime, &update.MessageID)
+ }
+}
+
+func runreminder(id string) {
+ // Get reminder
+ r := reminders[id]
+ remaining := r.When - time.Now().Unix()
+ if remaining > 0 {
+ // Wait remaining time
+ time.Sleep(time.Second * time.Duration(remaining))
+ }
+ // Remind!
+ broker.SendTextMessage(&tg.APIChat{ChatID: r.TargetID}, "Heyla! Mi avevi chiesto di ricordarti questo:\n"+r.Text, nil)
+ // Delete reminder from pending list and save list to disk
+ delete(reminders, id)
+ savereminder()
+}
+
+func savereminder() {
+ file, err := os.Create(*remindpath)
+ if err != nil {
+ log.Println("[remind] WARN: Could not open pending reminders file: " + err.Error())
+ return
+ }
+ err = json.NewEncoder(file).Encode(reminders)
+ if err != nil {
+ log.Println("[macro] WARN: Could not save pending reminders into file: " + err.Error())
+ }
+}
+
+func isSscanfValid(n int, err error) bool {
+ return err == nil
+}
+
+func parseDuration(date string) (time.Time, error) {
+ now := time.Now()
+ num := 0
+ sep := ' '
+ hour := now.Hour()
+ min := now.Minute()
+ sec := now.Second()
+ day := now.Day()
+ month := now.Month()
+ year := now.Year()
+ switch {
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d:%d", &day, &month, &year, &hour, &min, &sec)):
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d", &day, &month, &year, &hour, &min)):
+ sec = now.Second()
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d", &day, &month, &year)):
+ hour = now.Hour()
+ min = now.Minute()
+ sec = now.Second()
+ case isSscanfValid(fmt.Sscanf(date, "%d:%d:%d", &hour, &min, &sec)):
+ day = now.Day()
+ month = now.Month()
+ year = now.Year()
+ case isSscanfValid(fmt.Sscanf(date, "%d:%d", &hour, &min)):
+ day = now.Day()
+ month = now.Month()
+ year = now.Year()
+ sec = now.Second()
+ case isSscanfValid(fmt.Sscanf(date, "%d%c", &num, &sep)):
+ dur := time.Duration(num)
+ switch unicode.ToLower(sep) {
+ case 's':
+ dur *= time.Second
+ case 'm':
+ dur *= time.Minute
+ case 'h':
+ dur *= time.Hour
+ case 'd':
+ dur *= time.Hour * 24
+ default:
+ return now, fmt.Errorf("La durata ha una unità che non conosco, usa una di queste: s (secondi) m (minuti) h (ore) d (giorni)")
+ }
+ return now.Add(dur), nil
+ default:
+ return now, fmt.Errorf("Non capisco quando dovrei ricordartelo!")
+ }
+ targetDate := time.Date(year, month, day, hour, min, sec, 0, now.Location())
+ if targetDate.Before(now) {
+ return now, fmt.Errorf("Non posso ricordarti cose nel passato!")
+ }
+ if targetDate.After(now.Add(ReminderMaxDuration)) {
+ return now, fmt.Errorf("Non credo riuscirei a ricordarmi qualcosa per così tanto")
+ }
+ return targetDate, nil
+}