catbase/plugins/reminder/reminder.go

260 lines
5.9 KiB
Go
Raw Normal View History

2016-05-09 17:09:17 +00:00
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package reminder
import (
"fmt"
"sort"
2017-05-09 14:12:24 +00:00
"strconv"
2016-05-09 17:09:17 +00:00
"strings"
"sync"
"time"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
2017-05-01 15:54:44 +00:00
"github.com/velour/catbase/config"
2016-05-09 17:09:17 +00:00
)
type ReminderPlugin struct {
2017-05-09 14:12:24 +00:00
Bot bot.Bot
reminders []*Reminder
mutex *sync.Mutex
timer *time.Timer
config *config.Config
nextReminderId int
2016-05-09 17:09:17 +00:00
}
type Reminder struct {
2017-05-09 14:12:24 +00:00
id int
2016-05-09 17:09:17 +00:00
from string
who string
what string
when time.Time
channel string
}
type reminderSlice []*Reminder
func (s reminderSlice) Len() int {
return len(s)
}
func (s reminderSlice) Less(i, j int) bool {
return s[i].when.Before(s[j].when)
}
func (s reminderSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func New(bot bot.Bot) *ReminderPlugin {
dur, _ := time.ParseDuration("1h")
timer := time.NewTimer(dur)
timer.Stop()
plugin := &ReminderPlugin{
2017-05-09 14:12:24 +00:00
Bot: bot,
reminders: []*Reminder{},
mutex: &sync.Mutex{},
timer: timer,
config: bot.Config(),
nextReminderId: 0,
2016-05-09 17:09:17 +00:00
}
go reminderer(plugin)
return plugin
}
func reminderer(p *ReminderPlugin) {
//welcome to the reminderererererererererer
for {
<-p.timer.C
p.mutex.Lock()
reminder := p.reminders[0]
if len(p.reminders) >= 2 {
p.reminders = p.reminders[1:]
p.timer.Reset(p.reminders[0].when.Sub(time.Now()))
} else {
p.reminders = []*Reminder{}
}
p.mutex.Unlock()
2017-01-05 14:49:27 +00:00
2016-08-07 01:22:03 +00:00
if reminder.from == reminder.who {
reminder.from = "you"
}
2016-05-09 17:09:17 +00:00
message := fmt.Sprintf("Hey %s, %s wanted you to be reminded: %s", reminder.who, reminder.from, reminder.what)
p.Bot.SendMessage(reminder.channel, message)
}
}
func (p *ReminderPlugin) Message(message msg.Message) bool {
channel := message.Channel
from := message.User.Name
parts := strings.Fields(message.Body)
2016-05-09 17:27:28 +00:00
if len(parts) >= 5 {
2016-05-09 17:09:17 +00:00
if strings.ToLower(parts[0]) == "remind" {
who := parts[1]
2016-08-07 01:22:03 +00:00
if who == "me" {
who = from
}
2017-05-01 15:54:44 +00:00
2016-05-09 17:09:17 +00:00
dur, err := time.ParseDuration(parts[3])
if err != nil {
p.Bot.SendMessage(channel, "Easy cowboy, not sure I can parse that duration.")
return true
}
2017-05-01 15:54:44 +00:00
reminders := []*Reminder{}
operator := strings.ToLower(parts[2])
doConfirm := true
if operator == "in" {
//one off reminder
//remind who in dur blah
when := time.Now().Add(dur)
what := strings.Join(parts[4:], " ")
2017-05-09 14:12:24 +00:00
id := p.nextReminderId
p.nextReminderId++
2017-05-01 15:54:44 +00:00
reminders = append(reminders, &Reminder{
2017-05-09 14:12:24 +00:00
id: id,
2017-05-01 15:54:44 +00:00
from: from,
who: who,
what: what,
when: when,
channel: channel,
})
} else if operator == "every" && strings.ToLower(parts[4]) == "for" {
//batch add, especially for reminding msherms to buy a kit
//remind who every dur for dur2 blah
dur2, err := time.ParseDuration(parts[5])
if err != nil {
p.Bot.SendMessage(channel, "Easy cowboy, not sure I can parse that duration.")
return true
}
when := time.Now().Add(dur)
endTime := time.Now().Add(dur2)
what := strings.Join(parts[6:], " ")
for i := 0; when.Before(endTime); i++ {
if i >= p.config.Reminder.MaxBatchAdd {
p.Bot.SendMessage(channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.")
doConfirm = false
break
}
2017-05-09 14:12:24 +00:00
id := p.nextReminderId
p.nextReminderId++
2017-05-01 15:54:44 +00:00
reminders = append(reminders, &Reminder{
2017-05-09 14:12:24 +00:00
id: id,
2017-05-01 15:54:44 +00:00
from: from,
who: who,
what: what,
when: when,
channel: channel,
})
when = when.Add(dur)
}
} else {
p.Bot.SendMessage(channel, "Easy cowboy, not sure I comprehend what you're asking.")
return true
}
2016-05-09 17:09:17 +00:00
2017-05-01 15:54:44 +00:00
if doConfirm {
response := fmt.Sprintf("Sure %s, I'll remind %s.", from, who)
p.Bot.SendMessage(channel, response)
}
2016-05-09 17:09:17 +00:00
p.mutex.Lock()
p.timer.Stop()
2017-05-01 15:54:44 +00:00
p.reminders = append(p.reminders, reminders...)
2016-05-09 17:09:17 +00:00
sort.Sort(reminderSlice(p.reminders))
2017-05-01 15:54:44 +00:00
if len(p.reminders) > 0 {
p.timer.Reset(p.reminders[0].when.Sub(time.Now()))
}
2016-05-09 17:09:17 +00:00
p.mutex.Unlock()
return true
}
2017-04-27 16:47:18 +00:00
} else if len(parts) == 2 && strings.ToLower(parts[0]) == "list" && strings.ToLower(parts[1]) == "reminders" {
var response string
p.mutex.Lock()
if len(p.reminders) == 0 {
response = "no pending reminders"
} else {
counter := 1
for _, reminder := range p.reminders {
if reminder.channel == channel {
2017-05-09 14:12:24 +00:00
response += fmt.Sprintf("%d) %s -> %s :: %s @ %s (id=%d)\n", counter, reminder.from, reminder.who, reminder.what, reminder.when, reminder.id)
2017-04-27 16:47:18 +00:00
counter++
}
}
}
p.mutex.Unlock()
p.Bot.SendMessage(channel, response)
return true
2017-05-09 14:12:24 +00:00
} else if len(parts) == 3 && strings.ToLower(parts[0]) == "cancel" && strings.ToLower(parts[1]) == "reminder" {
id, err := strconv.Atoi(parts[2])
if err != nil {
p.Bot.SendMessage(channel, fmt.Sprintf("couldn't parse id: %s", parts[2]))
} else {
p.mutex.Lock()
deleted := false
for i, reminder := range p.reminders {
if reminder.id == id {
copy(p.reminders[i:], p.reminders[i+1:])
p.reminders[len(p.reminders)-1] = nil
p.reminders = p.reminders[:len(p.reminders)-1]
deleted = true
break
}
}
p.mutex.Unlock()
if deleted {
p.Bot.SendMessage(channel, fmt.Sprintf("successfully canceled reminder: %s", parts[2]))
} else {
p.Bot.SendMessage(channel, fmt.Sprintf("failed to find and cancel reminder: %s", parts[2]))
}
}
return true
2016-05-09 17:09:17 +00:00
}
return false
}
func (p *ReminderPlugin) Help(channel string, parts []string) {
p.Bot.SendMessage(channel, "Pester someone with a reminder. Try \"remind <user> in <duration> message\".\n\nUnsure about duration syntax? Check https://golang.org/pkg/time/#ParseDuration")
}
func (p *ReminderPlugin) Event(kind string, message msg.Message) bool {
return false
}
func (p *ReminderPlugin) BotMessage(message msg.Message) bool {
return false
}
func (p *ReminderPlugin) RegisterWeb() *string {
return nil
}