mirror of https://github.com/velour/catbase.git
176 lines
4.2 KiB
Go
176 lines
4.2 KiB
Go
package last
|
|
|
|
import (
|
|
"fmt"
|
|
bh "github.com/timshannon/bolthold"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/velour/catbase/config"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/velour/catbase/bot"
|
|
"github.com/velour/catbase/plugins/first"
|
|
)
|
|
|
|
type LastPlugin struct {
|
|
b bot.Bot
|
|
store *bh.Store
|
|
c *config.Config
|
|
|
|
handlers bot.HandlerTable
|
|
channels map[string]bool
|
|
}
|
|
|
|
func New(b bot.Bot) *LastPlugin {
|
|
p := &LastPlugin{
|
|
b: b,
|
|
store: b.Store(),
|
|
c: b.Config(),
|
|
channels: map[string]bool{},
|
|
}
|
|
p.register()
|
|
return p
|
|
}
|
|
|
|
func (p *LastPlugin) register() {
|
|
p.handlers = bot.HandlerTable{
|
|
{
|
|
Kind: bot.Message, IsCmd: true,
|
|
Regex: regexp.MustCompile(`(?i)^who killed the channel\??$`),
|
|
HelpText: "Find out who had Last yesterday",
|
|
Handler: p.whoKilled,
|
|
},
|
|
{
|
|
Kind: bot.Message, IsCmd: true,
|
|
Regex: regexp.MustCompile(`(?i)^who killed #?(?P<channel>\S+)\??$`),
|
|
HelpText: "Find out who had Last yesterday in a channel",
|
|
Handler: p.whoKilledChannel,
|
|
},
|
|
{
|
|
Kind: bot.Message, IsCmd: false,
|
|
Regex: regexp.MustCompile(`.*`),
|
|
HelpText: "Last does secret stuff you don't need to know about.",
|
|
Handler: p.recordLast,
|
|
},
|
|
}
|
|
p.b.RegisterTable(p, p.handlers)
|
|
}
|
|
|
|
func (p *LastPlugin) enabled_channel(r bot.Request) bool {
|
|
chs := p.c.GetArray("Last.channels", []string{})
|
|
for _, ch := range chs {
|
|
if r.Msg.Channel == ch {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func nextNoon(t time.Time) time.Duration {
|
|
day := first.Midnight(t)
|
|
nextNoon := day.Add(12 * time.Hour)
|
|
log.Debug().
|
|
Time("t", t).
|
|
Time("nextNoon", nextNoon).
|
|
Bool("before(t)", nextNoon.Before(t)).
|
|
Msgf("nextNoon")
|
|
if nextNoon.Before(t) {
|
|
nextNoon = nextNoon.Add(24 * time.Hour)
|
|
}
|
|
log.Debug().Msgf("nextNoon.Sub(t): %v", nextNoon.Sub(t))
|
|
return nextNoon.Sub(t)
|
|
}
|
|
|
|
func (p *LastPlugin) recordLast(r bot.Request) bool {
|
|
if !p.enabled_channel(r) {
|
|
return false
|
|
}
|
|
ch := r.Msg.Channel
|
|
who := r.Msg.User.Name
|
|
day := first.Midnight(time.Now())
|
|
|
|
if _, ok := p.channels[ch]; !ok {
|
|
if !p.b.OnBlacklist(ch, bot.PluginName(p)) {
|
|
p.channels[ch] = true
|
|
log.Debug().Msgf("Next Noon: %v", nextNoon(time.Now().UTC()))
|
|
time.AfterFunc(nextNoon(time.Now().Local()), p.reportLast(ch))
|
|
}
|
|
}
|
|
|
|
if r.Msg.Body == "" {
|
|
return false
|
|
}
|
|
|
|
invalidUsers := p.c.GetArray("Last.invalidUsers", []string{"unknown"})
|
|
for _, u := range invalidUsers {
|
|
if who == u {
|
|
return false
|
|
}
|
|
}
|
|
|
|
l := &Last{
|
|
Day: day.Unix(),
|
|
TS: time.Now().Unix(),
|
|
Channel: ch,
|
|
Who: who,
|
|
Message: r.Msg.Body,
|
|
}
|
|
|
|
// todo: I think Last might depend on a key being something more real
|
|
return p.store.Insert(bh.NextSequence(), l) == nil
|
|
}
|
|
|
|
type Last struct {
|
|
ID uint64 `boltholdKey:"ID"`
|
|
Day int64
|
|
TS int64 `db:"ts"`
|
|
Channel string `db:"channel"`
|
|
Who string `db:"who"`
|
|
Message string `db:"message"`
|
|
}
|
|
|
|
func (p *LastPlugin) yesterdaysLast(ch string) (Last, error) {
|
|
l := Last{}
|
|
midnight := first.Midnight(time.Now())
|
|
q := `select * from Last where channel = ? and day < ? and day >= ? order by day limit 1`
|
|
log.Debug().Str("q", q).Msgf("yesterdaysLast: %d to %d", midnight.Unix(), midnight.Add(-24*time.Hour).Unix())
|
|
err := p.store.FindOne(&l, bh.Where("Channel").Eq(ch).And("Day").Lt(midnight.Unix()).And("Day").Ge(midnight.Add(-24*time.Hour).Unix()))
|
|
if err != nil {
|
|
return Last{}, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
func (p *LastPlugin) reportLast(ch string) func() {
|
|
return func() {
|
|
p.sayLast(p.b.DefaultConnector(), ch, ch, false)
|
|
time.AfterFunc(24*time.Hour, p.reportLast(ch))
|
|
}
|
|
}
|
|
|
|
func (p *LastPlugin) whoKilled(r bot.Request) bool {
|
|
p.sayLast(r.Conn, r.Msg.Channel, r.Msg.Channel, true)
|
|
return true
|
|
}
|
|
|
|
func (p *LastPlugin) whoKilledChannel(r bot.Request) bool {
|
|
ch := r.Values["channel"]
|
|
p.sayLast(r.Conn, r.Conn.GetChannelID(ch), r.Msg.Channel, true)
|
|
return true
|
|
}
|
|
|
|
func (p *LastPlugin) sayLast(c bot.Connector, chFrom, chTo string, force bool) {
|
|
l, err := p.yesterdaysLast(chFrom)
|
|
if err != nil || l.Day == 0 {
|
|
log.Error().Err(err).Interface("Last", l).Msgf("Couldn't find Last")
|
|
if force {
|
|
p.b.Send(c, bot.Message, chTo, "I couldn't find a Last.")
|
|
}
|
|
return
|
|
}
|
|
msg := fmt.Sprintf(`%s killed the channel Last night by saying "%s"`, l.Who, l.Message)
|
|
p.b.Send(c, bot.Message, chTo, msg)
|
|
}
|