func parseReminder(from, body string) (*remind.Reminder, error) { parts := regexRemindMe.FindStringSubmatch(body) if len(parts) < 7 { err := errors.New("Could not schedule your reminder. Be sure to" + " use military time (24-hour time) when saying something like," + "\n\nRemind me to take out the trash @ 18:00 daily") log.Printf("Error sending after failed time parsing: %v\n", err) return nil, err } // len(parts) >= 7 // log.Printf("%d parts == %#v\n", len(parts), parts) // parts[0] is the entire SMS message; ignore description := parts[1] around := (parts[2] == "around") nextRun, err := parseTime(parts[3], parts[5]) if err != nil { return nil, err } impliedDaily := (parts[4] == "starting") var period time.Duration if impliedDaily || parts[6] == "daily" { period = 24 * time.Hour } var plusMinus time.Duration if around { // TODO: Make configurable plusMinus = 60 * time.Minute } reminder := &remind.Reminder{ Recipient: from, Description: strings.ToUpper(description[0:1]) + description[1:], NextRun: nextRun, Period: period, PlusMinus: plusMinus, Raw: body, Created: remind.Now(), } return reminder, nil }
func parseTime(hhmm string, day string) (time.Time, error) { when := strings.SplitN(hhmm, ":", 2) hours, _ := strconv.Atoi(when[0]) mins, _ := strconv.Atoi(when[1]) now := remind.Now() if day == "" || day == "today" || day == "tonight" || day == "tomorrow" { nowHours, nowMins, nowSecs := now.Clock() today := now. Add(time.Duration(-nowHours) * time.Hour). Add(time.Duration(-nowMins) * time.Minute). Add(time.Duration(-nowSecs) * time.Second) nextRun := today. Add(time.Duration(hours) * time.Hour). Add(time.Duration(mins) * time.Minute) if nextRun.Before(now) || day == "tomorrow" { // Tomorrow nextRun = nextRun.Add(24 * time.Hour) } return nextRun, nil } // Guaranteed: day is of the form `\d?\d/\d?\d` monthDay := strings.SplitN(day, "/", 2) month, _ := strconv.Atoi(monthDay[0]) dayNum, _ := strconv.Atoi(monthDay[1]) nextRun := time.Date(now.Year(), time.Month(month), dayNum, hours, mins, 0, 0, remind.LosAngeles) if nextRun.Before(now) { // Next year nextRun = time.Date(now.Year()+1, time.Month(month), dayNum, hours, mins, 0, 0, remind.LosAngeles) } return nextRun, nil }
func TestReminderRegex(t *testing.T) { nowYear, nowMonth, nowDay := remind.Now().Date() tests := []struct { body string rem remind.Reminder }{ { "Remind me to buy milk at 14:45 tomorrow", remind.Reminder{ Description: "Buy milk", NextRun: time.Date(nowYear, nowMonth, nowDay+1, 14, 45, 0, 0, remind.LosAngeles), }, }, { "Remind me to do $tuFF at 23:59 today", remind.Reminder{ Description: "Do $tuFF", NextRun: time.Date(nowYear, nowMonth, nowDay, 23, 59, 0, 0, remind.LosAngeles), }, }, { "Remind me to do whatever at 23:59", remind.Reminder{ Description: "Do whatever", NextRun: time.Date(nowYear, nowMonth, nowDay, 23, 59, 0, 0, remind.LosAngeles), }, }, { " remind me to write_GO/code!!.(?) @ 23:59 on 12/09", remind.Reminder{ Description: "Write_GO/code!!.(?)", NextRun: time.Date(nowYear, 12, 9, 23, 59, 0, 0, remind.LosAngeles), }, }, { "Remind me to take out the trash @ 18:00", remind.Reminder{ Description: "Take out the trash", NextRun: time.Date(nowYear, nowMonth, nowDay, 18, 00, 0, 0, remind.LosAngeles), }, }, { "Remind me to take out the trash @ 18:00 starting 1/1", remind.Reminder{ Description: "Take out the trash", NextRun: time.Date(nowYear+1, 1, 1, 18, 00, 0, 0, remind.LosAngeles), Period: 24 * time.Hour, }, }, { "Remind me to take out the trash @ 18:00 on 1/1 daily", remind.Reminder{ Description: "Take out the trash", NextRun: time.Date(nowYear+1, 1, 1, 18, 00, 0, 0, remind.LosAngeles), Period: 24 * time.Hour, }, }, } for _, test := range tests { r, err := parseReminder("", test.body) if err != nil { t.Errorf("Error parsing `%s`: %v", test.body, err) continue } assert.Equal(t, r.Description, test.rem.Description, "Description is wrong") assert.Equal(t, r.NextRun, test.rem.NextRun, "NextRun is wrong") assert.Equal(t, r.Period, test.rem.Period, "Period is wrong") assert.Equal(t, r.PlusMinus, time.Duration(0), "PlusMinus is wrong") r, _ = parseReminder("", test.body+" daily") assert.Equal(t, r.Period, 24*time.Hour, "Period is wrong (daily)") } }
func init() { rand.Seed(remind.Now().Unix()) }
func main() { if len(os.Args) < 5 { log.Fatalf("Usage: %s <to_number> <start_time> <finish_time> <msg 1> [<msg 2> ...]\n", filepath.Base(os.Args[0])) } toNumber := os.Args[1] startStr := os.Args[2] // \d?\d:\d\d finishStr := os.Args[3] // \d?\d:\d\d messages := os.Args[4:] if !regexTime.MatchString(startStr) { log.Fatalf("Start time must be of the form hh:mm; you typed '%s'", startStr) } if !regexTime.MatchString(finishStr) { log.Fatalf("Finish time must be of the form hh:mm; you typed '%s'", finishStr) } startHour, _ := strconv.Atoi(startStr[:2]) startMinute, _ := strconv.Atoi(startStr[3:]) finishHour, _ := strconv.Atoi(finishStr[:2]) finishMinute, _ := strconv.Atoi(finishStr[3:]) now := remind.Now() year, month, day := now.Date() start := time.Date(year, month, day, startHour, startMinute, 0, 0, remind.LosAngeles) finish := time.Date(year, month, day, finishHour, finishMinute, 0, 0, remind.LosAngeles) reminders, err := remind.IntervalSMS(toNumber, messages, start, finish) if err != nil { log.Fatalf("Error from IntervalSMS: %v\n", err) } wg := &sync.WaitGroup{} wg.Add(len(reminders)) for _, rem := range reminders { go func(rem *remind.Reminder) { defer wg.Done() if rem.NextRun.Before(now) { log.Printf("Reminder `%s` scheduled for the past (%s ago); not running\n", rem.Description, now.Sub(rem.NextRun)) return } sleep := rem.NextRun.Sub(now) log.Printf("Reminder `%s` will run in %s\n", rem.Description, sleep) time.Sleep(sleep) if err := rem.SendSMS(); err != nil { log.Printf("Error sending Reminder `%v` to %s: %v\n", rem.Description, rem.Recipient, err) return } log.Printf("Reminder `%s` sent successfully\n", rem.Description) }(rem) } wg.Wait() log.Printf("All %d Reminders finished; exiting\n", len(reminders)) }