mirror of https://github.com/velour/catbase.git
Compare commits
3 Commits
b13385774e
...
c68ca70964
Author | SHA1 | Date |
---|---|---|
Chris Sexton | c68ca70964 | |
Chris Sexton | ccfdb5a715 | |
Chris Sexton | b1f46d6517 |
2
main.go
2
main.go
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/velour/catbase/plugins/achievements"
|
"github.com/velour/catbase/plugins/achievements"
|
||||||
"github.com/velour/catbase/plugins/aoc"
|
"github.com/velour/catbase/plugins/aoc"
|
||||||
|
"github.com/velour/catbase/plugins/goals"
|
||||||
"github.com/velour/catbase/plugins/meme"
|
"github.com/velour/catbase/plugins/meme"
|
||||||
"github.com/velour/catbase/plugins/sms"
|
"github.com/velour/catbase/plugins/sms"
|
||||||
"github.com/velour/catbase/plugins/twitter"
|
"github.com/velour/catbase/plugins/twitter"
|
||||||
|
@ -121,6 +122,7 @@ func main() {
|
||||||
b.AddPlugin(remember.New(b))
|
b.AddPlugin(remember.New(b))
|
||||||
b.AddPlugin(your.New(b))
|
b.AddPlugin(your.New(b))
|
||||||
b.AddPlugin(counter.New(b))
|
b.AddPlugin(counter.New(b))
|
||||||
|
b.AddPlugin(goals.New(b))
|
||||||
b.AddPlugin(reminder.New(b))
|
b.AddPlugin(reminder.New(b))
|
||||||
b.AddPlugin(babbler.New(b))
|
b.AddPlugin(babbler.New(b))
|
||||||
b.AddPlugin(zork.New(b))
|
b.AddPlugin(zork.New(b))
|
||||||
|
|
|
@ -75,6 +75,12 @@ func New(b bot.Bot) *BeersPlugin {
|
||||||
|
|
||||||
p.registerWeb()
|
p.registerWeb()
|
||||||
|
|
||||||
|
token := p.c.Get("Untappd.Token", "NONE")
|
||||||
|
if token == "NONE" || token == "" {
|
||||||
|
log.Error().Msgf("No untappd token. Checking disabled.")
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
for _, channel := range p.c.GetArray("Untappd.Channels", []string{}) {
|
for _, channel := range p.c.GetArray("Untappd.Channels", []string{}) {
|
||||||
go p.untappdLoop(b.DefaultConnector(), channel)
|
go p.untappdLoop(b.DefaultConnector(), channel)
|
||||||
}
|
}
|
||||||
|
@ -229,7 +235,7 @@ func (p *BeersPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserBeers(db *sqlx.DB, user string) counter.Item {
|
func getUserBeers(db *sqlx.DB, user string) counter.Item {
|
||||||
booze, _ := counter.GetItem(db, user, itemName)
|
booze, _ := counter.GetUserItem(db, user, itemName)
|
||||||
return booze
|
return booze
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
package beers
|
package beers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/velour/catbase/plugins/cli"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/velour/catbase/plugins/cli"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/velour/catbase/bot"
|
"github.com/velour/catbase/bot"
|
||||||
"github.com/velour/catbase/bot/msg"
|
"github.com/velour/catbase/bot/msg"
|
||||||
|
@ -40,7 +41,7 @@ func makeBeersPlugin(t *testing.T) (*BeersPlugin, *bot.MockBot) {
|
||||||
|
|
||||||
func TestCounter(t *testing.T) {
|
func TestCounter(t *testing.T) {
|
||||||
_, mb := makeBeersPlugin(t)
|
_, mb := makeBeersPlugin(t)
|
||||||
i, err := counter.GetItem(mb.DB(), "tester", "test")
|
i, err := counter.GetUserItem(mb.DB(), "tester", "test")
|
||||||
if !assert.Nil(t, err) {
|
if !assert.Nil(t, err) {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
|
@ -55,7 +56,7 @@ func TestImbibe(t *testing.T) {
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
b.message(makeMessage("!imbibe"))
|
b.message(makeMessage("!imbibe"))
|
||||||
assert.Len(t, mb.Messages, 2)
|
assert.Len(t, mb.Messages, 2)
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, it.Count)
|
assert.Equal(t, 2, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,7 @@ func TestEq(t *testing.T) {
|
||||||
b, mb := makeBeersPlugin(t)
|
b, mb := makeBeersPlugin(t)
|
||||||
b.message(makeMessage("!beers = 3"))
|
b.message(makeMessage("!beers = 3"))
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 3, it.Count)
|
assert.Equal(t, 3, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ func TestEqNeg(t *testing.T) {
|
||||||
b, mb := makeBeersPlugin(t)
|
b, mb := makeBeersPlugin(t)
|
||||||
b.message(makeMessage("!beers = -3"))
|
b.message(makeMessage("!beers = -3"))
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, it.Count)
|
assert.Equal(t, 0, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -83,7 +84,7 @@ func TestEqZero(t *testing.T) {
|
||||||
b.message(makeMessage("!beers = 0"))
|
b.message(makeMessage("!beers = 0"))
|
||||||
assert.Len(t, mb.Messages, 2)
|
assert.Len(t, mb.Messages, 2)
|
||||||
assert.Contains(t, mb.Messages[1], "reversal of fortune")
|
assert.Contains(t, mb.Messages[1], "reversal of fortune")
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, it.Count)
|
assert.Equal(t, 0, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,7 @@ func TestBeersPlusEq(t *testing.T) {
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
b.message(makeMessage("beers += 5"))
|
b.message(makeMessage("beers += 5"))
|
||||||
assert.Len(t, mb.Messages, 2)
|
assert.Len(t, mb.Messages, 2)
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 10, it.Count)
|
assert.Equal(t, 10, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -102,11 +103,11 @@ func TestBeersPlusEq(t *testing.T) {
|
||||||
func TestPuke(t *testing.T) {
|
func TestPuke(t *testing.T) {
|
||||||
b, mb := makeBeersPlugin(t)
|
b, mb := makeBeersPlugin(t)
|
||||||
b.message(makeMessage("beers += 5"))
|
b.message(makeMessage("beers += 5"))
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 5, it.Count)
|
assert.Equal(t, 5, it.Count)
|
||||||
b.message(makeMessage("puke"))
|
b.message(makeMessage("puke"))
|
||||||
it, err = counter.GetItem(mb.DB(), "tester", itemName)
|
it, err = counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, it.Count)
|
assert.Equal(t, 0, it.Count)
|
||||||
}
|
}
|
||||||
|
@ -114,7 +115,7 @@ func TestPuke(t *testing.T) {
|
||||||
func TestBeersReport(t *testing.T) {
|
func TestBeersReport(t *testing.T) {
|
||||||
b, mb := makeBeersPlugin(t)
|
b, mb := makeBeersPlugin(t)
|
||||||
b.message(makeMessage("beers += 5"))
|
b.message(makeMessage("beers += 5"))
|
||||||
it, err := counter.GetItem(mb.DB(), "tester", itemName)
|
it, err := counter.GetUserItem(mb.DB(), "tester", itemName)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 5, it.Count)
|
assert.Equal(t, 5, it.Count)
|
||||||
b.message(makeMessage("beers"))
|
b.message(makeMessage("beers"))
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
|
||||||
"github.com/velour/catbase/bot"
|
"github.com/velour/catbase/bot"
|
||||||
"github.com/velour/catbase/bot/msg"
|
"github.com/velour/catbase/bot/msg"
|
||||||
)
|
)
|
||||||
|
@ -138,8 +139,33 @@ func MkAlias(db *sqlx.DB, aliasName, pointsTo string) (*alias, error) {
|
||||||
return &alias{db, id, aliasName, pointsTo}, nil
|
return &alias{db, id, aliasName, pointsTo}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetItem returns a specific counter for a subject
|
// GetUserItem returns a specific counter for all subjects
|
||||||
func GetItem(db *sqlx.DB, nick, itemName string) (Item, error) {
|
func GetItem(db *sqlx.DB, itemName string) ([]Item, error) {
|
||||||
|
itemName = trimUnicode(itemName)
|
||||||
|
var items []Item
|
||||||
|
var a alias
|
||||||
|
if err := db.Get(&a, `select * from counter_alias where item=?`, itemName); err == nil {
|
||||||
|
itemName = a.PointsTo
|
||||||
|
} else {
|
||||||
|
log.Error().Err(err).Interface("alias", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.Select(&items, `select * from counter where item= ?`, itemName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug().
|
||||||
|
Str("itemName", itemName).
|
||||||
|
Interface("items", items).
|
||||||
|
Msg("got item")
|
||||||
|
for _, i := range items {
|
||||||
|
i.DB = db
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserItem returns a specific counter for a subject
|
||||||
|
func GetUserItem(db *sqlx.DB, nick, itemName string) (Item, error) {
|
||||||
itemName = trimUnicode(itemName)
|
itemName = trimUnicode(itemName)
|
||||||
var item Item
|
var item Item
|
||||||
item.DB = db
|
item.DB = db
|
||||||
|
@ -169,6 +195,7 @@ func GetItem(db *sqlx.DB, nick, itemName string) (Item, error) {
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserItem returns a specific counter for a subject
|
||||||
// Create saves a counter
|
// Create saves a counter
|
||||||
func (i *Item) Create() error {
|
func (i *Item) Create() error {
|
||||||
res, err := i.Exec(`insert into counter (nick, item, count) values (?, ?, ?);`,
|
res, err := i.Exec(`insert into counter (nick, item, count) values (?, ?, ?);`,
|
||||||
|
@ -197,6 +224,9 @@ func (i *Item) Update(value int) error {
|
||||||
Int("value", value).
|
Int("value", value).
|
||||||
Msg("Updating item")
|
Msg("Updating item")
|
||||||
_, err := i.Exec(`update counter set count = ? where id = ?`, i.Count, i.ID)
|
_, err := i.Exec(`update counter set count = ? where id = ?`, i.Count, i.ID)
|
||||||
|
if err == nil {
|
||||||
|
sendUpdate(i.Nick, i.Item, i.Count)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +266,7 @@ func New(b bot.Bot) *CounterPlugin {
|
||||||
b.Register(cp, bot.Message, cp.message)
|
b.Register(cp, bot.Message, cp.message)
|
||||||
b.Register(cp, bot.Help, cp.help)
|
b.Register(cp, bot.Help, cp.help)
|
||||||
cp.registerWeb()
|
cp.registerWeb()
|
||||||
|
|
||||||
return cp
|
return cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +402,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
subject := strings.ToLower(nick)
|
subject := strings.ToLower(nick)
|
||||||
itemName := strings.ToLower(parts[1])
|
itemName := strings.ToLower(parts[1])
|
||||||
|
|
||||||
it, err := GetItem(p.DB, subject, itemName)
|
it, err := GetUserItem(p.DB, subject, itemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -412,7 +443,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
}
|
}
|
||||||
|
|
||||||
var item Item
|
var item Item
|
||||||
item, err := GetItem(p.DB, subject, itemName)
|
item, err := GetUserItem(p.DB, subject, itemName)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("I don't think %s has any %s.",
|
p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("I don't think %s has any %s.",
|
||||||
|
@ -450,7 +481,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
|
|
||||||
if strings.HasSuffix(parts[0], "++") {
|
if strings.HasSuffix(parts[0], "++") {
|
||||||
// ++ those fuckers
|
// ++ those fuckers
|
||||||
item, err := GetItem(p.DB, subject, itemName)
|
item, err := GetUserItem(p.DB, subject, itemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -467,7 +498,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
return true
|
return true
|
||||||
} else if strings.HasSuffix(parts[0], "--") {
|
} else if strings.HasSuffix(parts[0], "--") {
|
||||||
// -- those fuckers
|
// -- those fuckers
|
||||||
item, err := GetItem(p.DB, subject, itemName)
|
item, err := GetUserItem(p.DB, subject, itemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -498,7 +529,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
|
|
||||||
if parts[1] == "+=" {
|
if parts[1] == "+=" {
|
||||||
// += those fuckers
|
// += those fuckers
|
||||||
item, err := GetItem(p.DB, subject, itemName)
|
item, err := GetUserItem(p.DB, subject, itemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -516,7 +547,7 @@ func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
|
||||||
return true
|
return true
|
||||||
} else if parts[1] == "-=" {
|
} else if parts[1] == "-=" {
|
||||||
// -= those fuckers
|
// -= those fuckers
|
||||||
item, err := GetItem(p.DB, subject, itemName)
|
item, err := GetUserItem(p.DB, subject, itemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -560,7 +591,7 @@ func (p *CounterPlugin) checkMatch(c bot.Connector, message msg.Message) bool {
|
||||||
itemName := strings.ToLower(submatches[1])
|
itemName := strings.ToLower(submatches[1])
|
||||||
|
|
||||||
// We will specifically allow :tea: to keep compatability
|
// We will specifically allow :tea: to keep compatability
|
||||||
item, err := GetItem(p.DB, nick, itemName)
|
item, err := GetUserItem(p.DB, nick, itemName)
|
||||||
if err != nil || (item.Count == 0 && item.Item != ":tea:") {
|
if err != nil || (item.Count == 0 && item.Item != ":tea:") {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -625,7 +656,7 @@ func (p *CounterPlugin) handleCounterAPI(w http.ResponseWriter, r *http.Request)
|
||||||
w.Write(j)
|
w.Write(j)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := GetItem(p.DB, info.User, info.Thing)
|
item, err := GetUserItem(p.DB, info.User, info.Thing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -661,3 +692,24 @@ func (p *CounterPlugin) handleCounterAPI(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, string(data))
|
fmt.Fprint(w, string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Update struct {
|
||||||
|
Who string
|
||||||
|
What string
|
||||||
|
Amount int
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateFunc func(Update)
|
||||||
|
|
||||||
|
var updateFuncs = []updateFunc{}
|
||||||
|
|
||||||
|
func RegisterUpdate(f updateFunc) {
|
||||||
|
log.Debug().Msgf("registering update func")
|
||||||
|
updateFuncs = append(updateFuncs, f)
|
||||||
|
}
|
||||||
|
func sendUpdate(who, what string, amount int) {
|
||||||
|
log.Debug().Msgf("sending updates to %d places", len(updateFuncs))
|
||||||
|
for _, f := range updateFuncs {
|
||||||
|
f(Update{who, what, amount})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ func TestMkAlias(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("mkalias fuck mornings"))
|
c.message(makeMessage("mkalias fuck mornings"))
|
||||||
c.message(makeMessage("fuck++"))
|
c.message(makeMessage("fuck++"))
|
||||||
item, err := GetItem(mb.DB(), "tester", "mornings")
|
item, err := GetUserItem(mb.DB(), "tester", "mornings")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, item.Count)
|
assert.Equal(t, 1, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func TestRmAlias(t *testing.T) {
|
||||||
c.message(makeMessage("mkalias fuck mornings"))
|
c.message(makeMessage("mkalias fuck mornings"))
|
||||||
c.message(makeMessage("rmalias fuck"))
|
c.message(makeMessage("rmalias fuck"))
|
||||||
c.message(makeMessage("fuck++"))
|
c.message(makeMessage("fuck++"))
|
||||||
item, err := GetItem(mb.DB(), "tester", "mornings")
|
item, err := GetUserItem(mb.DB(), "tester", "mornings")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, item.Count)
|
assert.Equal(t, 0, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ func TestThreeSentencesExists(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage(":beer:++"))
|
c.message(makeMessage(":beer:++"))
|
||||||
c.message(makeMessage(":beer:. Earl Grey. Hot."))
|
c.message(makeMessage(":beer:. Earl Grey. Hot."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":beer:")
|
item, err := GetUserItem(mb.DB(), "tester", ":beer:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, item.Count)
|
assert.Equal(t, 2, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ func TestThreeSentencesExists(t *testing.T) {
|
||||||
func TestThreeSentencesNotExists(t *testing.T) {
|
func TestThreeSentencesNotExists(t *testing.T) {
|
||||||
mb, c := setup(t)
|
mb, c := setup(t)
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
item, err := GetItem(mb.DB(), "tester", ":beer:")
|
item, err := GetUserItem(mb.DB(), "tester", ":beer:")
|
||||||
c.message(makeMessage(":beer:. Earl Grey. Hot."))
|
c.message(makeMessage(":beer:. Earl Grey. Hot."))
|
||||||
item, err = GetItem(mb.DB(), "tester", ":beer:")
|
item, err = GetUserItem(mb.DB(), "tester", ":beer:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, item.Count)
|
assert.Equal(t, 0, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func TestTeaEarlGreyHot(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("Tea. Earl Grey. Hot."))
|
c.message(makeMessage("Tea. Earl Grey. Hot."))
|
||||||
c.message(makeMessage("Tea. Earl Grey. Hot."))
|
c.message(makeMessage("Tea. Earl Grey. Hot."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, item.Count)
|
assert.Equal(t, 2, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func TestTeaTwoPeriods(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("Tea. Earl Grey."))
|
c.message(makeMessage("Tea. Earl Grey."))
|
||||||
c.message(makeMessage("Tea. Earl Grey."))
|
c.message(makeMessage("Tea. Earl Grey."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, item.Count)
|
assert.Equal(t, 0, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func TestTeaMultiplePeriods(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("Tea. Earl Grey. Spiked. Hot."))
|
c.message(makeMessage("Tea. Earl Grey. Spiked. Hot."))
|
||||||
c.message(makeMessage("Tea. Earl Grey. Spiked. Hot."))
|
c.message(makeMessage("Tea. Earl Grey. Spiked. Hot."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, item.Count)
|
assert.Equal(t, 2, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ func TestTeaGreenHot(t *testing.T) {
|
||||||
c.message(makeMessage("Tea. Green. Hot."))
|
c.message(makeMessage("Tea. Green. Hot."))
|
||||||
c.message(makeMessage("Tea. Green. Hot"))
|
c.message(makeMessage("Tea. Green. Hot"))
|
||||||
c.message(makeMessage("Tea. Green. Iced."))
|
c.message(makeMessage("Tea. Green. Iced."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 3, item.Count)
|
assert.Equal(t, 3, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ func TestTeaUnrelated(t *testing.T) {
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("Tea."))
|
c.message(makeMessage("Tea."))
|
||||||
c.message(makeMessage("Tea. It's great."))
|
c.message(makeMessage("Tea. It's great."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, item.Count)
|
assert.Equal(t, 0, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ func TestTeaSkieselQuote(t *testing.T) {
|
||||||
mb, c := setup(t)
|
mb, c := setup(t)
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("blah, this is a whole page of explanation where \"we did local search and used a tabu list\" would have sufficed"))
|
c.message(makeMessage("blah, this is a whole page of explanation where \"we did local search and used a tabu list\" would have sufficed"))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, item.Count)
|
assert.Equal(t, 0, item.Count)
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ func TestTeaUnicodeJapanese(t *testing.T) {
|
||||||
mb, c := setup(t)
|
mb, c := setup(t)
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
c.message(makeMessage("Tea. おちや. Hot."))
|
c.message(makeMessage("Tea. おちや. Hot."))
|
||||||
item, err := GetItem(mb.DB(), "tester", ":tea:")
|
item, err := GetUserItem(mb.DB(), "tester", ":tea:")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, item.Count)
|
assert.Equal(t, 1, item.Count)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,309 @@
|
||||||
|
package goals
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"github.com/velour/catbase/bot"
|
||||||
|
"github.com/velour/catbase/bot/msg"
|
||||||
|
"github.com/velour/catbase/config"
|
||||||
|
"github.com/velour/catbase/plugins/counter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoalsPlugin struct {
|
||||||
|
b bot.Bot
|
||||||
|
cfg *config.Config
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(b bot.Bot) *GoalsPlugin {
|
||||||
|
p := &GoalsPlugin{
|
||||||
|
b: b,
|
||||||
|
cfg: b.Config(),
|
||||||
|
db: b.DB(),
|
||||||
|
}
|
||||||
|
p.mkDB()
|
||||||
|
b.Register(p, bot.Message, p.message)
|
||||||
|
b.Register(p, bot.Help, p.help)
|
||||||
|
counter.RegisterUpdate(p.update)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) mkDB() {
|
||||||
|
_, err := p.db.Exec(`create table if not exists goals (
|
||||||
|
id integer primary key,
|
||||||
|
kind string not null,
|
||||||
|
who string not null,
|
||||||
|
what string not null,
|
||||||
|
amount integer,
|
||||||
|
|
||||||
|
unique (who, what, kind)
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Msgf("could not create goals db: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var registerSelf = regexp.MustCompile(`(?i)^register (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)\s?(?P<amount>[[:digit:]]+)?`)
|
||||||
|
var registerOther = regexp.MustCompile(`(?i)^register (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+)\s?(?P<amount>[[:digit:]]+)?`)
|
||||||
|
var deRegisterSelf = regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`)
|
||||||
|
var deRegisterOther = regexp.MustCompile(`(?i)^deregister (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>.*)`)
|
||||||
|
var checkSelf = regexp.MustCompile(`(?i)^check (?P<type>competition|goal) (?P<what>[[:punct:][:alnum:]]+)`)
|
||||||
|
var checkOther = regexp.MustCompile(`(?i)^check (?P<type>competition|goal) for (?P<who>[[:punct:][:alnum:]]+) (?P<what>[[:punct:][:alnum:]]+)`)
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) message(conn bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||||
|
body := strings.TrimSpace(message.Body)
|
||||||
|
who := message.User.Name
|
||||||
|
ch := message.Channel
|
||||||
|
|
||||||
|
if registerOther.MatchString(body) {
|
||||||
|
c := parseCmd(registerOther, body)
|
||||||
|
amount, _ := strconv.Atoi(c["amount"])
|
||||||
|
p.register(conn, ch, c["type"], c["what"], c["who"], amount)
|
||||||
|
} else if registerSelf.MatchString(body) {
|
||||||
|
c := parseCmd(registerSelf, body)
|
||||||
|
amount, _ := strconv.Atoi(c["amount"])
|
||||||
|
p.register(conn, ch, c["type"], c["what"], who, amount)
|
||||||
|
} else if deRegisterOther.MatchString(body) {
|
||||||
|
c := parseCmd(deRegisterOther, body)
|
||||||
|
p.deregister(conn, ch, c["type"], c["what"], c["who"])
|
||||||
|
} else if deRegisterSelf.MatchString(body) {
|
||||||
|
c := parseCmd(deRegisterSelf, body)
|
||||||
|
p.deregister(conn, ch, c["type"], c["what"], who)
|
||||||
|
} else if checkOther.MatchString(body) {
|
||||||
|
c := parseCmd(checkOther, body)
|
||||||
|
p.check(conn, ch, c["type"], c["what"], c["who"])
|
||||||
|
} else if checkSelf.MatchString(body) {
|
||||||
|
c := parseCmd(checkSelf, body)
|
||||||
|
p.check(conn, ch, c["type"], c["what"], who)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) register(c bot.Connector, ch, kind, what, who string, howMuch int) {
|
||||||
|
if kind == "goal" && howMuch == 0 {
|
||||||
|
p.b.Send(c, bot.Message, ch,
|
||||||
|
fmt.Sprintf("%s, you need to have a goal amount if you want to have a goal for %s.", who, what))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g := p.newGoal(kind, who, what, howMuch)
|
||||||
|
err := g.Save()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not create goal")
|
||||||
|
p.b.Send(c, bot.Message, ch, "I couldn't create that goal for some reason.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("%s created", kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) deregister(c bot.Connector, ch, kind, what, who string) {
|
||||||
|
g, err := p.getGoalKind(kind, who, what)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not find goal to delete")
|
||||||
|
p.b.Send(c, bot.Message, ch, "I couldn't find that item to deregister.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = g.Delete()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not delete goal")
|
||||||
|
p.b.Send(c, bot.Message, ch, "I couldn't deregister that.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("%s %s deregistered", kind, what))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) check(c bot.Connector, ch, kind, what, who string) {
|
||||||
|
if kind == "goal" {
|
||||||
|
p.checkGoal(c, ch, what, who)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.checkCompetition(c, ch, what, who)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) checkCompetition(c bot.Connector, ch, what, who string) {
|
||||||
|
items, err := counter.GetItem(p.db, what)
|
||||||
|
if err != nil || len(items) == 0 {
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("I couldn't find any %s", what))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
if items[i].Count == items[j].Count && who == items[i].Nick {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if items[i].Count > items[j].Count {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if items[0].Nick == who && len(items) > 1 && items[1].Count == items[0].Count {
|
||||||
|
p.b.Send(c, bot.Message, ch,
|
||||||
|
fmt.Sprintf("Congratulations! You're in the lead for %s with %d, but you're tied with %s",
|
||||||
|
what, items[0].Count, items[1].Nick))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if items[0].Nick == who {
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("Congratulations! You're in the lead for %s with %d.",
|
||||||
|
what, items[0].Count))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, i := range items {
|
||||||
|
if i.Nick == who {
|
||||||
|
count = i.Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("%s is in the lead for %s with %d. You have %d to catch up.",
|
||||||
|
items[0].Nick, what, items[0].Count, items[0].Count-count))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) checkGoal(c bot.Connector, ch, what, who string) {
|
||||||
|
g, err := p.getGoalKind("goal", who, what)
|
||||||
|
if err != nil {
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("I couldn't find %s", what))
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := counter.GetUserItem(p.db, who, what)
|
||||||
|
if err != nil {
|
||||||
|
p.b.Send(c, bot.Message, ch, fmt.Sprintf("I couldn't find any %s", what))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
perc := float64(item.Count) / float64(g.Amount) * 100.0
|
||||||
|
|
||||||
|
if perc >= 100 {
|
||||||
|
p.deregister(c, ch, g.Kind, g.What, g.Who)
|
||||||
|
m := fmt.Sprintf("You made it! You have %.2f%% of %s and now it's done.", perc, what)
|
||||||
|
p.b.Send(c, bot.Message, ch, m)
|
||||||
|
} else {
|
||||||
|
m := fmt.Sprintf("You have %d out of %d for %s. You're %.2f%% of the way there!",
|
||||||
|
item.Count, g.Amount, what, perc)
|
||||||
|
p.b.Send(c, bot.Message, ch, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||||
|
ch := message.Channel
|
||||||
|
msg := "Goals can set goals and competition for your counters."
|
||||||
|
msg += fmt.Sprintf("\nRegister with `%s` for yourself", registerSelf)
|
||||||
|
msg += fmt.Sprintf("\nRegister with `%s` for other people", registerOther)
|
||||||
|
msg += fmt.Sprintf("\nDeregister with `%s` for yourself", deRegisterSelf)
|
||||||
|
msg += fmt.Sprintf("\nDeregister with `%s` for other people", deRegisterOther)
|
||||||
|
msg += fmt.Sprintf("\nCheck with `%s` for yourself", checkSelf)
|
||||||
|
msg += fmt.Sprintf("\nCheck with `%s` for other people", checkOther)
|
||||||
|
p.b.Send(c, bot.Message, ch, msg)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type cmd map[string]string
|
||||||
|
|
||||||
|
func parseCmd(r *regexp.Regexp, body string) cmd {
|
||||||
|
out := cmd{}
|
||||||
|
subs := r.FindStringSubmatch(body)
|
||||||
|
if len(subs) == 0 {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
for i, n := range r.SubexpNames() {
|
||||||
|
out[n] = subs[i]
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type goal struct {
|
||||||
|
ID int64
|
||||||
|
Kind string
|
||||||
|
Who string
|
||||||
|
What string
|
||||||
|
Amount int
|
||||||
|
|
||||||
|
gp *GoalsPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) newGoal(kind, who, what string, amount int) goal {
|
||||||
|
return goal{
|
||||||
|
ID: -1,
|
||||||
|
Kind: kind,
|
||||||
|
Who: who,
|
||||||
|
What: what,
|
||||||
|
Amount: amount,
|
||||||
|
gp: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) getGoal(who, what string) ([]*goal, error) {
|
||||||
|
gs := []*goal{}
|
||||||
|
err := p.db.Select(&gs, `select * from goals where who = ? and what = ?`,
|
||||||
|
who, what)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, g := range gs {
|
||||||
|
g.gp = p
|
||||||
|
}
|
||||||
|
return gs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) getGoalKind(kind, who, what string) (*goal, error) {
|
||||||
|
g := &goal{gp: p}
|
||||||
|
err := p.db.Get(g, `select * from goals where kind = ? and who = ? and what = ?`,
|
||||||
|
kind, who, what)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *goal) Save() error {
|
||||||
|
res, err := g.gp.db.Exec(`insert or replace into goals (who, what, kind, amount) values (?, ?, ? ,?)`,
|
||||||
|
g.Who, g.What, g.Kind, g.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dbID, err := res.LastInsertId()
|
||||||
|
if err == nil && dbID != g.ID && g.ID == 0 {
|
||||||
|
g.ID = dbID
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g goal) Delete() error {
|
||||||
|
if g.ID == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := g.gp.db.Exec(`delete from goals where id = ?`, g.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoalsPlugin) update(u counter.Update) {
|
||||||
|
log.Debug().Msgf("entered update for %#v", u)
|
||||||
|
gs, err := p.getGoal(u.Who, u.What)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("could not get goal for %#v", u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chs := p.cfg.GetArray("channels", []string{})
|
||||||
|
c := p.b.DefaultConnector()
|
||||||
|
for _, g := range gs {
|
||||||
|
for _, ch := range chs {
|
||||||
|
if g.Kind == "goal" {
|
||||||
|
p.checkGoal(c, ch, u.What, u.Who)
|
||||||
|
} else {
|
||||||
|
p.checkCompetition(c, ch, u.What, u.Who)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue