diff --git a/plugins/reminder/reminder.go b/plugins/reminder/reminder.go index d4003e9..a3b23b8 100644 --- a/plugins/reminder/reminder.go +++ b/plugins/reminder/reminder.go @@ -20,6 +20,7 @@ import ( "github.com/velour/catbase/bot" "github.com/velour/catbase/bot/msg" "github.com/velour/catbase/config" + "github.com/velour/catbase/plugins/sms" ) const ( @@ -367,6 +368,10 @@ func reminderer(c bot.Connector, p *ReminderPlugin) { } p.bot.Send(c, bot.Message, reminder.channel, message) + smsPlugin := sms.New(p.bot) + if err := smsPlugin.Send(reminder.who, message); err != nil { + log.Error().Err(err).Msgf("could not send reminder") + } if err := p.deleteReminder(reminder.id); err != nil { log.Error(). diff --git a/plugins/sms/sms.go b/plugins/sms/sms.go index ff15d47..9fc94a8 100644 --- a/plugins/sms/sms.go +++ b/plugins/sms/sms.go @@ -19,8 +19,6 @@ var plugin *SMSPlugin type SMSPlugin struct { b bot.Bot c *config.Config - - knownUsers map[string]string } func New(b bot.Bot) *SMSPlugin { @@ -28,10 +26,11 @@ func New(b bot.Bot) *SMSPlugin { return plugin } plugin = &SMSPlugin{ - b: b, - c: b.Config(), - knownUsers: make(map[string]string), + b: b, + c: b.Config(), } + plugin.setup() + plugin.registerWeb() b.Register(plugin, bot.Message, plugin.message) b.Register(plugin, bot.Help, plugin.help) return plugin @@ -47,31 +46,48 @@ func (p *SMSPlugin) checkNumber(num string) (string, error) { return num, nil } -var regRegex = regexp.MustCompile(`^my sms number is (\d+)$`) +var regRegex = regexp.MustCompile(`(?i)my sms number is (\d+)`) +var sendRegex = regexp.MustCompile(`(?i)send sms to\s(?P\S+)\s(?P.+)`) // Send will send a text to a registered user, who func (p *SMSPlugin) Send(who, message string) error { - num, ok := p.knownUsers[who] - if !ok { - return fmt.Errorf("unknown user: %s", who) + num, err := p.get(who) + if err != nil { + return fmt.Errorf("unknown user: %s: %w", who, err) } sid := p.c.Get("TWILIOSID", "") token := p.c.Get("TWILIOTOKEN", "") myNum := p.c.Get("TWILIONUMBER", "") client := twilio.NewClient(sid, token, nil) - _, err := client.Messages.SendMessage(myNum, num, message, nil) + _, err = client.Messages.SendMessage(myNum, num, message, nil) return err } func (p *SMSPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { - ch := message.Channel - lowerBody := strings.ToLower(strings.TrimSpace(message.Body)) + ch, chName := message.Channel, message.ChannelName + body := strings.TrimSpace(message.Body) + who := message.User.Name - log.Debug().Msgf("lowerBody: %s, match: %v", lowerBody, regRegex.MatchString(lowerBody)) + log.Debug().Bytes("body", []byte(body)).Msgf("body: '%s', match: %v, %#v", body, sendRegex.MatchString(body), sendRegex.FindStringSubmatch(body)) - if lowerBody == "send me a message" { - err := p.Send(message.User.Name, "Hey, here's a message.") + if strings.ToLower(body) == "delete my sms" { + if err := p.delete(who); err != nil { + p.b.Send(c, bot.Message, ch, "Something went wrong.") + return true + } + p.b.Send(c, bot.Message, ch, "Okay.") + return true + } + + if sendRegex.MatchString(body) { + subs := sendRegex.FindStringSubmatch(body) + if len(subs) != 3 { + p.b.Send(c, bot.Message, ch, fmt.Sprintf("I didn't send the message. Your request makes no sense.")) + return true + } + user, body := subs[1], fmt.Sprintf("#%s: %s", chName, subs[2]) + err := p.Send(user, body) if err != nil { p.b.Send(c, bot.Message, ch, fmt.Sprintf("I didn't send the message, %s", err)) return true @@ -80,10 +96,11 @@ func (p *SMSPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, return true } - if regRegex.MatchString(lowerBody) { - subs := regRegex.FindStringSubmatch(lowerBody) + if regRegex.MatchString(body) { + subs := regRegex.FindStringSubmatch(body) if subs == nil || len(subs) != 2 { - p.b.Send(c, bot.Message, ch, fmt.Sprintf("if you're trying to register a number, give me a message of the format: `%s`", regRegex)) + p.b.Send(c, bot.Message, ch, fmt.Sprintf("if you're trying to register a number, give me a "+ + "message of the format: `%s`", regRegex)) return true } @@ -93,11 +110,20 @@ func (p *SMSPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, return true } me := p.b.WhoAmI() - m := fmt.Sprintf("Hello from %s", me) - p.knownUsers[message.User.Name] = num - err = p.Send(message.User.Name, m) + m := fmt.Sprintf("Hello from %s. You are registered for reminders and you can chat with the channel "+ + "by talking to this number", me) + err = p.add(who, num) if err != nil { - delete(p.knownUsers, message.User.Name) + log.Error().Err(err).Msgf("error adding user") + p.b.Send(c, bot.Message, ch, fmt.Sprintf("I didn't send the message, %s", err)) + return true + } + err = p.Send(who, m) + if err != nil { + log.Error().Err(err).Msgf("error sending to user") + if err := p.delete(who); err != nil { + log.Error().Err(err).Msgf("error deleting user") + } p.b.Send(c, bot.Message, ch, fmt.Sprintf("I didn't send the message, %s", err)) return true } @@ -114,9 +140,83 @@ func (p *SMSPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, ar } func (p *SMSPlugin) registerWeb() { - http.HandleFunc("/sms/recieve", p.receive) + http.HandleFunc("/sms/new", p.receive) } func (p *SMSPlugin) receive(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + w.WriteHeader(405) + fmt.Fprintf(w, "Incorrect HTTP method") + return + } + if err := r.ParseForm(); err != nil { + log.Error().Err(err).Msgf("could not parse incoming SMS") + return + } + + body := r.PostFormValue("Body") + number := strings.TrimPrefix(r.PostFormValue("From"), "+1") + from, err := p.getByNumber(number) + if err != nil { + log.Debug().Err(err).Msgf("could not find user") + from = number + } + + m := fmt.Sprintf("SMS From %s: %s", from, body) + chs := p.c.GetArray("channels", []string{}) + + log.Debug().Msgf("%s to %v", m, chs) + + for _, ch := range chs { + p.b.Send(p.b.DefaultConnector(), bot.Message, ch, m) + } +} + +func (p *SMSPlugin) add(who, num string) error { + tx, err := p.b.DB().Beginx() + if err != nil { + return err + } + _, err = tx.Exec(`insert or replace into sms (who, num) values (?, ?)`, who, num) + if err != nil { + return err + } + err = tx.Commit() + return err +} + +func (p *SMSPlugin) delete(who string) error { + tx, err := p.b.DB().Beginx() + if err != nil { + return err + } + _, err = tx.Exec(`delete from sms where who=?`, who) + if err != nil { + return err + } + err = tx.Commit() + return err +} + +func (p *SMSPlugin) get(who string) (string, error) { + num := "" + err := p.b.DB().Get(&num, `select num from sms where who=?`, who) + return num, err +} + +func (p *SMSPlugin) getByNumber(num string) (string, error) { + who := "" + err := p.b.DB().Get(&who, `select who from sms where num=?`, num) + return who, err +} + +func (p *SMSPlugin) setup() { + _, err := p.b.DB().Exec(`create table if not exists sms ( + who string primary key, + num string + )`) + if err != nil { + log.Fatal().Err(err).Msgf("could not create sms table") + } }