catbase/plugins/last/last.go

195 lines
4.5 KiB
Go
Raw Normal View History

2021-04-27 16:36:34 +00:00
package last
import (
"fmt"
"regexp"
"time"
2021-04-28 16:07:59 +00:00
"github.com/velour/catbase/config"
2021-04-27 16:36:34 +00:00
"github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/plugins/first"
)
type LastPlugin struct {
b bot.Bot
db *sqlx.DB
2021-04-28 16:07:59 +00:00
c *config.Config
2021-04-27 16:36:34 +00:00
handlers bot.HandlerTable
channels map[string]bool
}
func New(b bot.Bot) *LastPlugin {
p := &LastPlugin{
b: b,
db: b.DB(),
2021-04-28 16:49:49 +00:00
c: b.Config(),
2021-04-27 16:36:34 +00:00
channels: map[string]bool{},
}
if err := p.migrate(); err != nil {
panic(err)
}
p.register()
return p
}
func (p *LastPlugin) migrate() error {
tx, err := p.db.Beginx()
if err != nil {
return err
}
_, err = tx.Exec(`create table if not exists last (
2021-04-28 21:13:03 +00:00
day integer,
2021-04-27 16:36:34 +00:00
channel string not null,
2021-04-28 21:13:03 +00:00
ts int not null,
2021-04-27 16:36:34 +00:00
who string not null,
2021-04-28 21:13:03 +00:00
message string not null,
constraint last_key primary key (day, channel) on conflict replace
2021-04-27 16:36:34 +00:00
)`)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
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,
},
2021-04-27 16:36:34 +00:00
}
p.b.RegisterTable(p, p.handlers)
}
2021-08-26 14:03:22 +00:00
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
}
2021-04-27 16:36:34 +00:00
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 {
2021-08-26 14:03:22 +00:00
if !p.enabled_channel(r) {
return false
}
2021-04-27 16:36:34 +00:00
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))
}
}
2021-04-28 16:07:59 +00:00
if r.Msg.Body == "" {
return false
}
invalidUsers := p.c.GetArray("last.invalidUsers", []string{"unknown"})
for _, u := range invalidUsers {
if who == u {
return false
}
}
2021-04-27 16:36:34 +00:00
_, err := p.db.Exec(
2021-04-28 21:13:03 +00:00
`insert into last values (?, ?, ?, ?, ?)`,
day.Unix(), ch, time.Now().Unix(), who, r.Msg.Body)
2021-04-27 16:36:34 +00:00
if err != nil {
log.Error().Err(err).Msgf("Could not record last.")
}
return false
}
type last struct {
Day int64
TS int64
Channel string
Who string
Message string
}
func (p *LastPlugin) yesterdaysLast(ch string) (last, error) {
2021-04-27 16:36:34 +00:00
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.db.Get(&l, q, ch, midnight.Unix(), midnight.Add(-24*time.Hour).Unix())
2021-04-27 16:36:34 +00:00
if err != nil {
return l, err
}
return l, nil
}
func (p *LastPlugin) reportLast(ch string) func() {
return func() {
p.sayLast(p.b.DefaultConnector(), ch, ch, false)
2021-04-27 16:36:34 +00:00
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)
2021-04-27 16:36:34 +00:00
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.")
}
2021-04-27 16:36:34 +00:00
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)
2021-04-27 16:36:34 +00:00
}