mirror of https://github.com/velour/catbase.git
last: create plugin
This commit is contained in:
parent
e290760e44
commit
4c669e520f
|
@ -243,7 +243,7 @@ func (b *bot) RegisterTable(p Plugin, handlers HandlerTable) {
|
||||||
|
|
||||||
// RegisterRegex does what register does, but with a matcher
|
// RegisterRegex does what register does, but with a matcher
|
||||||
func (b *bot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) {
|
func (b *bot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) {
|
||||||
t := reflect.TypeOf(p).String()
|
t := PluginName(p)
|
||||||
if _, ok := b.callbacks[t]; !ok {
|
if _, ok := b.callbacks[t]; !ok {
|
||||||
b.callbacks[t] = make(map[Kind][]HandlerSpec)
|
b.callbacks[t] = make(map[Kind][]HandlerSpec)
|
||||||
}
|
}
|
||||||
|
@ -354,7 +354,7 @@ func (b *bot) GetWhitelist() []string {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bot) onBlacklist(channel, plugin string) bool {
|
func (b *bot) OnBlacklist(channel, plugin string) bool {
|
||||||
return b.pluginBlacklist[channel+plugin]
|
return b.pluginBlacklist[channel+plugin]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,3 +365,8 @@ func (b *bot) onWhitelist(plugin string) bool {
|
||||||
func pluginNameStem(name string) string {
|
func pluginNameStem(name string) string {
|
||||||
return strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
|
return strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PluginName(p Plugin) string {
|
||||||
|
t := reflect.TypeOf(p).String()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (b *bot) Receive(conn Connector, kind Kind, msg msg.Message, args ...interf
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range b.pluginOrdering {
|
for _, name := range b.pluginOrdering {
|
||||||
if b.onBlacklist(msg.Channel, pluginNameStem(name)) || !b.onWhitelist(pluginNameStem(name)) {
|
if b.OnBlacklist(msg.Channel, pluginNameStem(name)) || !b.onWhitelist(pluginNameStem(name)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if b.runCallback(conn, b.plugins[name], kind, msg, args...) {
|
if b.runCallback(conn, b.plugins[name], kind, msg, args...) {
|
||||||
|
|
|
@ -159,6 +159,9 @@ type Bot interface {
|
||||||
|
|
||||||
// Get the contents of the white list
|
// Get the contents of the white list
|
||||||
GetWhitelist() []string
|
GetWhitelist() []string
|
||||||
|
|
||||||
|
// Check if a particular plugin is blacklisted
|
||||||
|
OnBlacklist(string, string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connector represents a server connection to a chat service
|
// Connector represents a server connection to a chat service
|
||||||
|
|
|
@ -121,4 +121,5 @@ func (mb *MockBot) GetPluginNames() []string { return nil }
|
||||||
func (mb *MockBot) RefreshPluginBlacklist() error { return nil }
|
func (mb *MockBot) RefreshPluginBlacklist() error { return nil }
|
||||||
func (mb *MockBot) RefreshPluginWhitelist() error { return nil }
|
func (mb *MockBot) RefreshPluginWhitelist() error { return nil }
|
||||||
func (mb *MockBot) GetWhitelist() []string { return []string{} }
|
func (mb *MockBot) GetWhitelist() []string { return []string{} }
|
||||||
|
func (mb *MockBot) OnBlacklist(ch, p string) bool { return false }
|
||||||
func (mb *MockBot) URLFormat(title, url string) string { return title + url }
|
func (mb *MockBot) URLFormat(title, url string) string { return title + url }
|
||||||
|
|
2
main.go
2
main.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/velour/catbase/connectors/discord"
|
"github.com/velour/catbase/connectors/discord"
|
||||||
"github.com/velour/catbase/plugins/giphy"
|
"github.com/velour/catbase/plugins/giphy"
|
||||||
"github.com/velour/catbase/plugins/gpt2"
|
"github.com/velour/catbase/plugins/gpt2"
|
||||||
|
"github.com/velour/catbase/plugins/last"
|
||||||
|
|
||||||
"github.com/velour/catbase/plugins/achievements"
|
"github.com/velour/catbase/plugins/achievements"
|
||||||
"github.com/velour/catbase/plugins/aoc"
|
"github.com/velour/catbase/plugins/aoc"
|
||||||
|
@ -120,6 +121,7 @@ func main() {
|
||||||
b.AddPlugin(giphy.New(b))
|
b.AddPlugin(giphy.New(b))
|
||||||
b.AddPlugin(gpt2.New(b))
|
b.AddPlugin(gpt2.New(b))
|
||||||
b.AddPlugin(emojifyme.New(b))
|
b.AddPlugin(emojifyme.New(b))
|
||||||
|
b.AddPlugin(last.New(b))
|
||||||
b.AddPlugin(first.New(b))
|
b.AddPlugin(first.New(b))
|
||||||
b.AddPlugin(leftpad.New(b))
|
b.AddPlugin(leftpad.New(b))
|
||||||
b.AddPlugin(talker.New(b))
|
b.AddPlugin(talker.New(b))
|
||||||
|
|
|
@ -83,7 +83,7 @@ func New(b bot.Bot) *FirstPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msgf("First plugin initialized with day: %s",
|
log.Info().Msgf("First plugin initialized with day: %s",
|
||||||
midnight(time.Now()))
|
Midnight(time.Now()))
|
||||||
|
|
||||||
fp := &FirstPlugin{
|
fp := &FirstPlugin{
|
||||||
bot: b,
|
bot: b,
|
||||||
|
@ -135,9 +135,9 @@ func getLastFirst(db *sqlx.DB, channel string) (*FirstEntry, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func midnight(t time.Time) time.Time {
|
func Midnight(t time.Time) time.Time {
|
||||||
y, m, d := t.Date()
|
y, m, d := t.Date()
|
||||||
return time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
|
return time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isNotToday(f *FirstEntry) bool {
|
func isNotToday(f *FirstEntry) bool {
|
||||||
|
@ -145,8 +145,8 @@ func isNotToday(f *FirstEntry) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
t := f.time
|
t := f.time
|
||||||
t0 := midnight(t)
|
t0 := Midnight(t)
|
||||||
return t0.Before(midnight(time.Now()))
|
return t0.Before(Midnight(time.Now()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *FirstPlugin) register() {
|
func (p *FirstPlugin) register() {
|
||||||
|
@ -281,7 +281,7 @@ func (p *FirstPlugin) recordFirst(c bot.Connector, message msg.Message) {
|
||||||
Str("body", message.Body).
|
Str("body", message.Body).
|
||||||
Msg("Recording first")
|
Msg("Recording first")
|
||||||
first := &FirstEntry{
|
first := &FirstEntry{
|
||||||
day: midnight(time.Now()),
|
day: Midnight(time.Now()),
|
||||||
time: message.Time,
|
time: message.Time,
|
||||||
channel: message.Channel,
|
channel: message.Channel,
|
||||||
body: message.Body,
|
body: message.Body,
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
package last
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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
|
||||||
|
|
||||||
|
handlers bot.HandlerTable
|
||||||
|
channels map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(b bot.Bot) *LastPlugin {
|
||||||
|
p := &LastPlugin{
|
||||||
|
b: b,
|
||||||
|
db: b.DB(),
|
||||||
|
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 (
|
||||||
|
day integer primary key,
|
||||||
|
ts int not null,
|
||||||
|
channel string not null,
|
||||||
|
who string not null,
|
||||||
|
message string not null
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LastPlugin) register() {
|
||||||
|
p.handlers = bot.HandlerTable{
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: false,
|
||||||
|
Regex: regexp.MustCompile(`.*`),
|
||||||
|
HelpText: "Last does secret stuff you don't need to know about.",
|
||||||
|
Handler: p.recordLast,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: bot.Message, IsCmd: true,
|
||||||
|
Regex: regexp.MustCompile(`(?i)^who killed the channel\??$`),
|
||||||
|
HelpText: "Find out who had last yesterday",
|
||||||
|
Handler: p.whoKilled,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p.b.RegisterTable(p, p.handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := p.db.Exec(
|
||||||
|
`insert into last values (?, ?, ?, ?, ?)
|
||||||
|
on conflict(day) do update set
|
||||||
|
ts=excluded.ts, channel=excluded.channel, who=excluded.who, message=excluded.message
|
||||||
|
where day=excluded.day`,
|
||||||
|
day.Unix(), time.Now().Unix(), ch, who, r.Msg.Body)
|
||||||
|
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() (last, error) {
|
||||||
|
l := last{}
|
||||||
|
midnight := first.Midnight(time.Now())
|
||||||
|
q := `select * from last where day < ? order by day limit 1`
|
||||||
|
err := p.db.Get(&l, q, midnight)
|
||||||
|
if err != nil {
|
||||||
|
return l, err
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LastPlugin) reportLast(ch string) func() {
|
||||||
|
return func() {
|
||||||
|
p.sayLast(p.b.DefaultConnector(), ch)
|
||||||
|
time.AfterFunc(24*time.Hour, p.reportLast(ch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LastPlugin) whoKilled(r bot.Request) bool {
|
||||||
|
p.sayLast(r.Conn, r.Msg.Channel)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LastPlugin) sayLast(c bot.Connector, ch string) {
|
||||||
|
l, err := p.yesterdaysLast()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Couldn't find last")
|
||||||
|
p.b.Send(c, bot.Message, ch, "I couldn't find a last.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l.Day == 0 {
|
||||||
|
log.Error().Interface("l", l).Msgf("Couldn't find last")
|
||||||
|
p.b.Send(c, bot.Message, ch, "I couldn't find a last.")
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf(`%s killed the channel last night by saying "%s"`, l.Who, l.Message)
|
||||||
|
p.b.Send(c, bot.Message, ch, msg)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package last
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNextNoonBeforeNoon(t *testing.T) {
|
||||||
|
log.Logger = log.Logger.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
t0 := time.Date(2021, 04, 27, 10, 10, 0, 0, time.Local)
|
||||||
|
t1 := nextNoon(t0)
|
||||||
|
expected := 1*time.Hour + 50*time.Minute
|
||||||
|
assert.Equal(t, expected, t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNextNoonAfterNoon(t *testing.T) {
|
||||||
|
log.Logger = log.Logger.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||||
|
t0 := time.Date(2021, 04, 27, 14, 15, 0, 0, time.Local)
|
||||||
|
t1 := nextNoon(t0)
|
||||||
|
expected := 21*time.Hour + 45*time.Minute
|
||||||
|
assert.Equal(t, expected, t1)
|
||||||
|
}
|
Loading…
Reference in New Issue