catbase/plugins/counter/counter.go

725 lines
19 KiB
Go
Raw Normal View History

2016-03-19 15:44:27 +00:00
package counter
2013-01-23 21:25:04 +00:00
import (
"database/sql"
2013-01-23 21:25:04 +00:00
"fmt"
2021-12-20 17:40:10 +00:00
bh "github.com/timshannon/bolthold"
2021-12-21 04:31:19 +00:00
"math"
2019-02-12 16:03:24 +00:00
"math/rand"
"regexp"
2017-01-23 15:10:54 +00:00
"strconv"
2013-01-23 21:25:04 +00:00
"strings"
2019-03-07 16:35:42 +00:00
"github.com/rs/zerolog/log"
"github.com/velour/catbase/config"
2019-03-07 16:35:42 +00:00
2016-01-17 18:00:44 +00:00
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
2013-01-23 21:25:04 +00:00
)
// This is a counter plugin to count arbitrary things.
type CounterPlugin struct {
2021-12-20 17:40:10 +00:00
b bot.Bot
store *bh.Store
cfg *config.Config
2013-01-23 21:25:04 +00:00
}
type Item struct {
2021-12-21 04:31:19 +00:00
store *bh.Store
created bool
2016-03-19 18:02:46 +00:00
2021-12-21 04:31:19 +00:00
ID uint64 `boltholdKey:"ID"`
Nick string
Item string
Count int
UserID string
2013-01-23 21:25:04 +00:00
}
2021-12-29 13:25:42 +00:00
type CounterAlias struct {
2021-12-21 04:31:19 +00:00
store *bh.Store
created bool
2018-01-04 17:23:59 +00:00
2021-12-21 04:31:19 +00:00
ID uint64 `boltholdKey:"ID"`
2018-01-04 17:23:59 +00:00
Item string
2021-12-22 03:36:08 +00:00
PointsTo string `db:"points_to"`
2018-01-04 17:23:59 +00:00
}
2019-05-27 18:27:34 +00:00
// GetItems returns all counters
2021-12-20 17:40:10 +00:00
func GetAllItems(store *bh.Store) ([]Item, error) {
2019-05-27 18:27:34 +00:00
var items []Item
2021-12-20 17:40:10 +00:00
err := store.Find(&items, &bh.Query{})
2019-05-27 18:27:34 +00:00
if err != nil {
return nil, err
}
// Don't forget to embed the db into all of that shiz
2019-05-27 18:27:34 +00:00
for i := range items {
2021-12-21 04:31:19 +00:00
items[i].store = store
2019-05-27 18:27:34 +00:00
}
return items, nil
}
2016-03-19 18:27:02 +00:00
// GetItems returns all counters for a subject
2021-12-29 13:25:42 +00:00
func GetItems(store *bh.Store, nick, id string) ([]*Item, error) {
var items []*Item
var err error
if id != "" {
2021-12-29 13:25:42 +00:00
if err = store.Find(&items, bh.Where("UserID").Eq(id)); err != nil {
return nil, err
}
} else {
2021-12-29 13:25:42 +00:00
if err = store.Find(&items, bh.Where("Nick").Eq(nick)); err != nil {
return nil, err
}
2016-03-19 18:02:46 +00:00
}
// Don't forget to embed the db into all of that shiz
2017-01-24 02:13:21 +00:00
for i := range items {
2021-12-21 04:31:19 +00:00
items[i].store = store
2016-03-19 18:02:46 +00:00
}
return items, nil
}
2021-12-20 17:40:10 +00:00
func LeaderAll(store *bh.Store) ([]Item, error) {
//s := `select id,item,nick,count from (select id,item,nick,count,max(abs(count)) from counter group by item having count(nick) > 1 and max(abs(count)) > 1) order by count desc`
// todo: translate that query
2018-01-04 12:39:24 +00:00
var items []Item
2021-12-20 17:40:10 +00:00
err := store.Find(&items, &bh.Query{})
2018-01-04 12:39:24 +00:00
if err != nil {
log.Error().Msgf("Error querying leaderboard: %s", err)
2018-01-04 12:39:24 +00:00
return nil, err
}
for i := range items {
2021-12-21 04:31:19 +00:00
items[i].store = store
2018-01-04 12:39:24 +00:00
}
return items, nil
}
2021-12-20 17:40:10 +00:00
func Leader(store *bh.Store, itemName string) ([]Item, error) {
2018-01-04 17:23:59 +00:00
itemName = strings.ToLower(itemName)
2021-12-20 17:40:10 +00:00
//s := `select * from counter where item=? order by count desc`
// todo: remove that when we verify this works
2018-01-04 12:39:24 +00:00
var items []Item
2021-12-21 04:31:19 +00:00
err := store.Find(&items, bh.Where("Item").Eq(itemName).SortBy("Count").Reverse())
2018-01-04 12:39:24 +00:00
if err != nil {
return nil, err
}
for i := range items {
2021-12-21 04:31:19 +00:00
items[i].store = store
2018-01-04 12:39:24 +00:00
}
return items, nil
}
2021-12-20 17:40:10 +00:00
func RmAlias(store *bh.Store, aliasName string) error {
2021-12-29 13:25:42 +00:00
a := &CounterAlias{}
2021-12-21 04:31:19 +00:00
if err := store.FindOne(a, bh.Where("Item").Eq(aliasName)); err != nil {
return err
}
2021-12-29 13:25:42 +00:00
return store.Delete(a.ID, CounterAlias{})
2020-04-20 10:20:21 +00:00
}
2021-12-29 13:25:42 +00:00
func MkAlias(store *bh.Store, aliasName, pointsTo string) (*CounterAlias, error) {
2020-04-20 10:20:21 +00:00
aliasName = strings.ToLower(aliasName)
2018-01-04 17:23:59 +00:00
pointsTo = strings.ToLower(pointsTo)
2021-12-29 13:25:42 +00:00
alias := &CounterAlias{
2021-12-21 04:31:19 +00:00
store: store,
2021-12-20 17:40:10 +00:00
Item: aliasName,
PointsTo: pointsTo,
2021-12-21 04:31:19 +00:00
created: true,
2018-01-04 17:23:59 +00:00
}
2021-12-20 17:40:10 +00:00
err := store.Insert(bh.NextSequence(), alias)
return alias, err
2018-01-04 17:23:59 +00:00
}
2020-05-26 15:38:55 +00:00
// GetUserItem returns a specific counter for all subjects
2021-12-20 17:40:10 +00:00
func GetItem(store *bh.Store, itemName string) ([]Item, error) {
2020-05-26 15:38:55 +00:00
itemName = trimUnicode(itemName)
var items []Item
2021-12-29 13:25:42 +00:00
var a CounterAlias
2021-12-21 04:31:19 +00:00
if err := store.FindOne(&a, bh.Where("Item").Eq(itemName)); err == nil {
2020-05-26 15:38:55 +00:00
itemName = a.PointsTo
} else {
log.Error().Err(err).Interface("alias", a)
}
2021-12-21 04:31:19 +00:00
err := store.Find(&items, bh.Where("Item").Eq(itemName))
2020-05-26 15:38:55 +00:00
if err != nil {
return nil, err
}
log.Debug().
Str("itemName", itemName).
Interface("items", items).
Msg("got item")
for _, i := range items {
2021-12-21 04:31:19 +00:00
i.store = store
2020-05-26 15:38:55 +00:00
}
return items, nil
}
// GetUserItem returns a specific counter for a subject
2021-12-21 04:31:19 +00:00
func GetUserItem(store *bh.Store, nick, id, itemName string) (*Item, error) {
2019-11-25 19:26:26 +00:00
itemName = trimUnicode(itemName)
2021-12-21 04:31:19 +00:00
item := &Item{}
item.Nick = nick
item.Item = itemName
item.UserID = id
2021-12-29 13:25:42 +00:00
var a CounterAlias
2021-12-21 04:31:19 +00:00
err := store.FindOne(&a, bh.Where("Item").Eq(itemName))
if err == nil {
log.Debug().Msgf("itemName now is %s", a.PointsTo)
2018-01-04 17:23:59 +00:00
itemName = a.PointsTo
2021-12-21 04:31:19 +00:00
item.Item = itemName
2018-01-04 17:23:59 +00:00
} else {
2021-12-21 04:31:19 +00:00
log.Error().Err(err).Interface("alias", a).Msgf("error finding alias")
2018-01-04 17:23:59 +00:00
}
if id != "" {
2021-12-21 04:31:19 +00:00
err = store.FindOne(item, bh.Where("UserID").Eq(id).And("Item").Eq(itemName))
} else {
err = store.FindOne(item, bh.Where("Nick").Eq(nick).And("Item").Eq(itemName))
2021-12-20 17:40:10 +00:00
}
2021-12-21 04:31:19 +00:00
if err != nil && err == bh.ErrNotFound {
log.Debug().Msgf("We gotta create this item: %v", item)
item.store = store
if err := item.Create(); err != nil {
return nil, err
}
return item, nil
} else if err != nil {
return nil, err
2016-03-19 18:02:46 +00:00
}
2021-12-21 04:31:19 +00:00
item.store = store
item.created = true
2019-03-07 16:35:42 +00:00
log.Debug().
Str("nick", nick).
Str("id", id).
2019-03-07 16:35:42 +00:00
Str("itemName", itemName).
Interface("item", item).
Msg("got item")
2016-03-19 18:02:46 +00:00
return item, nil
}
2020-05-26 15:38:55 +00:00
// GetUserItem returns a specific counter for a subject
2016-03-19 18:27:02 +00:00
// Create saves a counter
2016-03-19 18:02:46 +00:00
func (i *Item) Create() error {
2021-12-21 04:31:19 +00:00
log.Debug().Msgf("i %v, store %v", i, i.store)
err := i.store.Insert(bh.NextSequence(), i)
i.created = true
log.Debug().Msgf("we just inserted an item, %v", i)
2016-03-19 18:02:46 +00:00
return err
}
2016-03-19 18:27:02 +00:00
// UpdateDelta sets a value
// This will create or delete the item if necessary
2021-06-17 17:59:29 +00:00
func (i *Item) Update(r *bot.Request, value int) error {
2016-03-19 18:27:02 +00:00
i.Count = value
2021-12-21 04:31:19 +00:00
if i.Count == 0 && i.created {
2016-03-19 18:02:46 +00:00
return i.Delete()
}
2021-12-21 04:31:19 +00:00
if !i.created {
if err := i.Create(); err != nil {
log.Error().Err(err).Msg("could not create")
}
2016-03-19 18:02:46 +00:00
}
2019-03-07 16:35:42 +00:00
log.Debug().
Interface("i", i).
Int("value", value).
Msg("Updating item")
2021-12-21 04:31:19 +00:00
err := i.store.Update(i.ID, i)
2020-05-25 18:05:21 +00:00
if err == nil {
2021-06-17 17:59:29 +00:00
sendUpdate(r, i.Nick, i.Item, i.Count)
2020-05-25 18:05:21 +00:00
}
2016-03-19 18:02:46 +00:00
return err
}
2016-03-19 18:27:02 +00:00
// UpdateDelta changes a value according to some delta
// This will create or delete the item if necessary
2021-06-17 17:59:29 +00:00
func (i *Item) UpdateDelta(r *bot.Request, delta int) error {
2016-03-19 18:27:02 +00:00
i.Count += delta
2021-06-17 17:59:29 +00:00
return i.Update(r, i.Count)
2016-03-19 18:27:02 +00:00
}
// Delete removes a counter from the database
2016-03-19 18:02:46 +00:00
func (i *Item) Delete() error {
2021-12-21 04:31:19 +00:00
err := i.store.Delete(i.ID, Item{})
i.created = false
i.ID = math.MaxUint64
2016-03-19 18:02:46 +00:00
return err
}
func (p *CounterPlugin) migrate(r bot.Request) bool {
2021-12-20 17:40:10 +00:00
// todo: probably don't need this anymore
//db := p.db
//
//nicks := []string{}
//err := db.Select(&nicks, `select distinct nick from counter where userid is null`)
//if err != nil {
// log.Error().Err(err).Msg("could not get nick list")
// return false
//}
//
//log.Debug().Msgf("Migrating %d nicks to IDs", len(nicks))
//
//tx := db.MustBegin()
//
//for _, nick := range nicks {
// user, err := r.Conn.Profile(nick)
// if err != nil {
// continue
// }
// if _, err = tx.Exec(`update counter set userid=? where nick=?`, user.ID, nick); err != nil {
// log.Error().Err(err).Msg("Could not migrate users")
// continue
// }
//}
//
//if err := tx.Commit(); err != nil {
// log.Error().Err(err).Msg("Could not migrate users")
//}
return false
}
// NewCounterPlugin creates a new CounterPlugin with the Plugin interface
func New(b bot.Bot) *CounterPlugin {
cp := &CounterPlugin{
2021-12-20 17:40:10 +00:00
b: b,
store: b.Store(),
cfg: b.Config(),
2013-01-23 21:25:04 +00:00
}
2021-02-01 02:21:19 +00:00
b.RegisterRegex(cp, bot.Startup, regexp.MustCompile(`.*`), cp.migrate)
2021-02-01 02:21:19 +00:00
b.RegisterRegexCmd(cp, bot.Message, mkAliasRegex, cp.mkAliasCmd)
b.RegisterRegexCmd(cp, bot.Message, rmAliasRegex, cp.rmAliasCmd)
b.RegisterRegexCmd(cp, bot.Message, leaderboardRegex, cp.leaderboardCmd)
b.RegisterRegex(cp, bot.Message, teaRegex, cp.teaMatchCmd)
b.RegisterRegexCmd(cp, bot.Message, resetRegex, cp.resetCmd)
b.RegisterRegexCmd(cp, bot.Message, inspectRegex, cp.inspectCmd)
b.RegisterRegexCmd(cp, bot.Message, clearRegex, cp.clearCmd)
b.RegisterRegexCmd(cp, bot.Message, countRegex, cp.countCmd)
b.RegisterRegex(cp, bot.Message, incrementRegex, cp.incrementCmd)
b.RegisterRegex(cp, bot.Message, decrementRegex, cp.decrementCmd)
b.RegisterRegex(cp, bot.Message, addToRegex, cp.addToCmd)
b.RegisterRegex(cp, bot.Message, removeFromRegex, cp.removeFromCmd)
b.Register(cp, bot.Help, cp.help)
2019-05-27 18:27:34 +00:00
cp.registerWeb()
2020-05-25 18:05:21 +00:00
2021-11-16 01:34:09 +00:00
RegisterUpdate(func(r bot.Request, u Update) {
log.Debug().Msgf("Publishing update %v", u)
b.PubToASub("counter", u)
})
return cp
2013-01-23 21:25:04 +00:00
}
2019-11-25 19:26:26 +00:00
func trimUnicode(s string) string {
return strings.Trim(s, string(rune(0xFE0F)))
}
2021-02-01 02:21:19 +00:00
var mkAliasRegex = regexp.MustCompile(`(?i)^mkalias (?P<what>\S+) (?P<to>\S+)$`)
var rmAliasRegex = regexp.MustCompile(`(?i)^rmalias (?P<what>\S+)$`)
var leaderboardRegex = regexp.MustCompile(`(?i)^leaderboard\s?(?P<what>\S+)?$`)
var teaRegex = regexp.MustCompile("(?i)^([^.]+)\\. [^.]*\\. ([^.]*\\.?)+$")
var resetRegex = regexp.MustCompile(`(?i)^reset me$`)
var inspectRegex = regexp.MustCompile(`(?i)^inspect (?P<who>\S+)$`)
var clearRegex = regexp.MustCompile(`(?i)^clear (?P<what>\S+)$`)
var countRegex = regexp.MustCompile(`(?i)^count (?P<who>\S+)\s?(?P<what>\S+)?$`)
2021-02-01 02:51:28 +00:00
var incrementRegex = regexp.MustCompile(`(?i)^(?P<who>[^.\t\n\f\r ]+\s?\.)?(?P<thing>\S+)\s?\+\+$`)
var decrementRegex = regexp.MustCompile(`(?i)^(?P<who>[^.\t\n\f\r ]+\s?\.)?(?P<thing>\S+)\s?--$`)
2021-02-01 02:21:19 +00:00
var addToRegex = regexp.MustCompile(`(?i)^(?P<who>[^.\t\n\f\r ]+\s?\.)?(?P<thing>\S+)\s+\+=\s+(?P<amount>\d+)$`)
var removeFromRegex = regexp.MustCompile(`(?i)^(?P<who>[^.\t\n\f\r ]+\s?\.)?(?P<thing>\S+)\s+-=\s+(?P<amount>\d+)$`)
func (p *CounterPlugin) mkAliasCmd(r bot.Request) bool {
what := r.Values["what"]
to := r.Values["to"]
if what == "" || to == "" {
p.b.Send(r.Conn, bot.Message, fmt.Sprintf("You must provide all fields for an alias: %s", mkAliasRegex))
2021-02-01 02:21:19 +00:00
return true
2013-01-23 21:25:04 +00:00
}
2021-12-21 04:31:19 +00:00
alias, err := MkAlias(p.store, what, to)
if err != nil {
2021-02-01 02:21:19 +00:00
log.Error().Err(err).Msg("Could not mkalias")
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "We're gonna need too much db space to make an alias for your mom.")
2021-02-01 02:21:19 +00:00
return true
}
2021-12-21 04:31:19 +00:00
log.Debug().Msgf("alias created: %v", alias)
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("Created alias %s -> %s",
2021-02-01 02:21:19 +00:00
what, to))
return true
}
2013-01-23 21:25:04 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) rmAliasCmd(r bot.Request) bool {
what := r.Values["what"]
if what == "" {
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "You must specify an alias to remove.")
2018-01-04 17:23:59 +00:00
return true
2021-02-01 02:21:19 +00:00
}
2021-12-20 17:40:10 +00:00
if err := RmAlias(p.store, what); err != nil {
2021-02-01 02:21:19 +00:00
log.Error().Err(err).Msg("could not RmAlias")
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "`sudo rm your mom` => Nope, she's staying with me.")
2020-04-20 10:20:21 +00:00
return true
2021-02-01 02:21:19 +00:00
}
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "`sudo rm your mom`")
2021-02-01 02:21:19 +00:00
return true
}
2018-01-04 12:39:24 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) leaderboardCmd(r bot.Request) bool {
var cmd func() ([]Item, error)
itNameTxt := ""
what := r.Values["what"]
2018-01-04 12:39:24 +00:00
2021-02-01 02:21:19 +00:00
if what == "" {
2021-12-20 17:40:10 +00:00
cmd = func() ([]Item, error) { return LeaderAll(p.store) }
2021-02-01 02:21:19 +00:00
} else {
itNameTxt = fmt.Sprintf(" for %s", what)
2021-12-20 17:40:10 +00:00
cmd = func() ([]Item, error) { return Leader(p.store, what) }
2021-02-01 02:21:19 +00:00
}
its, err := cmd()
if err != nil {
log.Error().Err(err).Msg("Error with leaderboard")
return false
} else if len(its) == 0 {
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, "There are not enough entries for a leaderboard.")
2018-01-04 12:39:24 +00:00
return true
2021-02-01 02:21:19 +00:00
}
out := fmt.Sprintf("Leaderboard%s:\n", itNameTxt)
for _, it := range its {
out += fmt.Sprintf("%s with %d %s\n",
it.Nick,
it.Count,
it.Item,
)
}
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, out)
2021-02-01 02:21:19 +00:00
return true
}
func (p *CounterPlugin) resetCmd(r bot.Request) bool {
nick, id := p.resolveUser(r, "")
2021-02-01 02:21:19 +00:00
channel := r.Msg.Channel
2021-12-20 17:40:10 +00:00
items, err := GetItems(p.store, nick, id)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Msg("Error getting items to reset")
p.b.Send(r.Conn, bot.Message, channel, "Something is technically wrong with your counters.")
2017-01-24 02:13:21 +00:00
return true
2021-02-01 02:21:19 +00:00
}
log.Debug().Msgf("Items: %+v", items)
for _, item := range items {
item.Delete()
}
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s, you are as new, my son.", nick))
2021-02-01 02:21:19 +00:00
return true
}
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) inspectCmd(r bot.Request) bool {
who := r.Values["who"]
nick, id := "", ""
if who == "me" {
who = r.Msg.User.Name
nick, id = p.resolveUser(r, "")
2021-02-01 02:21:19 +00:00
} else {
nick, id = p.resolveUser(r, who)
2021-02-01 02:21:19 +00:00
}
channel := r.Msg.Channel
c := r.Conn
2021-02-01 02:21:19 +00:00
log.Debug().
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Msg("Getting counter")
// pull all of the items associated with "subject"
2021-12-20 17:40:10 +00:00
items, err := GetItems(p.store, nick, id)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Msg("Error retrieving items")
p.b.Send(c, bot.Message, channel, "Something went wrong finding that counter;")
2021-02-01 02:21:19 +00:00
return true
}
2021-12-21 04:31:19 +00:00
log.Debug().Msgf("items: %v", items)
resp := fmt.Sprintf("%s has the following counters:", nick)
2021-02-01 02:21:19 +00:00
count := 0
for _, it := range items {
count += 1
if count > 1 {
resp += ","
}
2021-02-01 02:21:19 +00:00
resp += fmt.Sprintf(" %s: %d", it.Item, it.Count)
if count > 20 {
resp += ", and a few others"
break
}
2021-02-01 02:21:19 +00:00
}
resp += "."
2021-02-01 02:21:19 +00:00
if count == 0 {
p.b.Send(c, bot.Message, channel, fmt.Sprintf("%s has no counters.", nick))
return true
2021-02-01 02:21:19 +00:00
}
p.b.Send(c, bot.Message, channel, resp)
2021-02-01 02:21:19 +00:00
return true
}
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) clearCmd(r bot.Request) bool {
nick, id := p.resolveUser(r, "")
2021-02-01 02:21:19 +00:00
itemName := strings.ToLower(r.Values["what"])
channel := r.Msg.Channel
c := r.Conn
2021-12-20 17:40:10 +00:00
it, err := GetUserItem(p.store, nick, id, itemName)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error getting item to remove")
p.b.Send(c, bot.Message, channel, "Something went wrong removing that counter;")
2021-02-01 02:21:19 +00:00
return true
}
err = it.Delete()
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error removing item")
p.b.Send(c, bot.Message, channel, "Something went wrong removing that counter;")
return true
2021-02-01 02:21:19 +00:00
}
p.b.Send(c, bot.Action, channel, fmt.Sprintf("chops a few %s out of his brain",
2021-02-01 02:21:19 +00:00
itemName))
return true
}
2013-01-23 21:25:04 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) countCmd(r bot.Request) bool {
itemName := strings.ToLower(r.Values["what"])
nick, id := r.Msg.User.Name, r.Msg.User.ID
if r.Values["what"] == "" {
itemName = r.Values["who"]
2021-02-01 02:21:19 +00:00
} else {
nick, id = p.resolveUser(r, r.Values["who"])
2021-02-01 02:21:19 +00:00
}
2013-01-23 21:25:04 +00:00
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
2021-02-01 02:21:19 +00:00
switch {
case err == sql.ErrNoRows:
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("I don't think %s has any %s.",
nick, itemName))
2013-01-23 21:25:04 +00:00
return true
2021-02-01 02:21:19 +00:00
case err != nil:
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error retrieving item count")
return true
}
2013-01-23 21:25:04 +00:00
p.b.Send(r.Conn, bot.Message, r.Msg.Channel, fmt.Sprintf("%s has %d %s.", nick, item.Count,
2021-02-01 02:21:19 +00:00
itemName))
2013-01-24 15:35:39 +00:00
2021-02-01 02:21:19 +00:00
return true
}
2017-01-17 22:57:39 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) incrementCmd(r bot.Request) bool {
nick, id := r.Msg.User.Name, r.Msg.User.ID
2021-02-01 02:21:19 +00:00
if r.Values["who"] != "" {
nick, id = p.resolveUser(r, r.Values["who"])
2021-02-01 02:21:19 +00:00
}
itemName := r.Values["thing"]
channel := r.Msg.Channel
// ++ those fuckers
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
2021-12-21 04:31:19 +00:00
log.Debug().Msgf("GetUserItem: %v", item)
if err != nil && err != bh.ErrNotFound {
2021-02-01 02:21:19 +00:00
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("error finding item")
// Item ain't there, I guess
return false
}
log.Debug().Msgf("About to update item: %#v", item)
2021-12-04 21:01:11 +00:00
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s has %d %s.", nick, item.Count+1, item.Item))
2021-06-17 17:59:29 +00:00
item.UpdateDelta(&r, 1)
2021-02-01 02:21:19 +00:00
return true
}
2017-01-17 22:57:39 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) decrementCmd(r bot.Request) bool {
nick, id := r.Msg.User.Name, r.Msg.User.ID
2021-02-01 02:21:19 +00:00
if r.Values["who"] != "" {
nick, id = p.resolveUser(r, r.Values["who"])
2021-02-01 02:21:19 +00:00
}
itemName := r.Values["thing"]
channel := r.Msg.Channel
// -- those fuckers
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error finding item")
// Item ain't there, I guess
return false
}
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s has %d %s.", nick,
2021-12-04 21:01:11 +00:00
item.Count-1, item.Item))
item.UpdateDelta(&r, -1)
2021-02-01 02:21:19 +00:00
return true
}
2017-01-17 22:57:39 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) addToCmd(r bot.Request) bool {
nick, id := p.resolveUser(r, r.Values["who"])
2021-02-01 02:21:19 +00:00
itemName := r.Values["thing"]
channel := r.Msg.Channel
// += those fuckers
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error finding item")
// Item ain't there, I guess
return false
2013-01-23 21:25:04 +00:00
}
2021-02-01 02:21:19 +00:00
n, _ := strconv.Atoi(r.Values["amount"])
log.Debug().Msgf("About to update item by %d: %#v", n, item)
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s has %d %s.", nick,
2021-12-04 21:01:11 +00:00
item.Count+n, item.Item))
item.UpdateDelta(&r, n)
2021-02-01 02:21:19 +00:00
return true
}
2013-01-23 21:25:04 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) removeFromCmd(r bot.Request) bool {
nick, id := p.resolveUser(r, r.Values["who"])
2021-02-01 02:21:19 +00:00
itemName := r.Values["thing"]
channel := r.Msg.Channel
// -= those fuckers
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
2021-02-01 02:21:19 +00:00
if err != nil {
log.Error().
Err(err).
Str("nick", nick).
Str("id", id).
2021-02-01 02:21:19 +00:00
Str("itemName", itemName).
Msg("Error finding item")
// Item ain't there, I guess
return false
}
n, _ := strconv.Atoi(r.Values["amount"])
log.Debug().Msgf("About to update item by -%d: %#v", n, item)
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s has %d %s.", nick,
2021-12-04 21:01:11 +00:00
item.Count-n, item.Item))
item.UpdateDelta(&r, -n)
2021-02-01 02:21:19 +00:00
return true
2013-01-23 21:25:04 +00:00
}
// Help responds to help requests. Every plugin must implement a help function.
2019-05-27 23:21:53 +00:00
func (p *CounterPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.b.Send(c, bot.Message, message.Channel, "You can set counters incrementally by using "+
2020-04-20 10:20:21 +00:00
"`<noun>++` and `<noun>--`. You can see all of your counters using "+
"`inspect`, erase them with `clear`, and view single counters with "+
"`count`.")
p.b.Send(c, bot.Message, message.Channel, "You can create aliases with `!mkalias <alias> <original>`")
p.b.Send(c, bot.Message, message.Channel, "You can remove aliases with `!rmalias <alias>`")
return true
}
2013-06-01 17:10:15 +00:00
2021-02-01 02:21:19 +00:00
func (p *CounterPlugin) teaMatchCmd(r bot.Request) bool {
nick, id := r.Msg.User.Name, r.Msg.User.ID
2021-02-01 02:21:19 +00:00
channel := r.Msg.Channel
2021-02-01 02:21:19 +00:00
submatches := teaRegex.FindStringSubmatch(r.Msg.Body)
if len(submatches) <= 1 {
return false
}
itemName := strings.ToLower(submatches[1])
// We will specifically allow :tea: to keep compatability
2021-12-20 17:40:10 +00:00
item, err := GetUserItem(p.store, nick, id, itemName)
if err != nil || (item.Count == 0 && item.Item != ":tea:") {
2019-03-07 16:35:42 +00:00
log.Error().
Err(err).
Str("itemName", itemName).
Msg("Error finding item")
// Item ain't there, I guess
return false
}
2019-03-07 16:35:42 +00:00
log.Debug().Msgf("About to update item: %#v", item)
delta := 1
if item.Count < 0 {
delta = -1
}
p.b.Send(r.Conn, bot.Message, channel, fmt.Sprintf("%s... %s has %d %s",
2021-12-04 21:01:11 +00:00
strings.Join(everyDayImShuffling([]string{"bleep", "bloop", "blop"}), "-"), nick, item.Count+delta, itemName))
item.UpdateDelta(&r, delta)
return true
}
2019-02-12 16:03:24 +00:00
func everyDayImShuffling(vals []string) []string {
ret := make([]string, len(vals))
perm := rand.Perm(len(vals))
for i, randIndex := range perm {
ret[i] = vals[randIndex]
}
return ret
}
2019-05-27 18:27:34 +00:00
2021-06-17 17:59:29 +00:00
type updateFunc func(bot.Request, Update)
2020-05-25 18:05:21 +00:00
var updateFuncs = []updateFunc{}
func RegisterUpdate(f updateFunc) {
log.Debug().Msgf("registering update func")
updateFuncs = append(updateFuncs, f)
}
2021-06-17 17:59:29 +00:00
func sendUpdate(r *bot.Request, who, what string, amount int) {
log.Debug().
Msgf("Updating %s for %s with %d", who, what, amount)
2021-06-17 17:59:29 +00:00
if r == nil {
return
}
2020-05-25 18:05:21 +00:00
log.Debug().Msgf("sending updates to %d places", len(updateFuncs))
for _, f := range updateFuncs {
2021-06-17 17:59:29 +00:00
f(*r, Update{who, what, amount})
2020-05-25 18:05:21 +00:00
}
}
func (p *CounterPlugin) resolveUser(r bot.Request, nick string) (string, string) {
id := ""
if nick != "" {
nick = strings.TrimSuffix(nick, ".")
u, err := r.Conn.Profile(nick)
if err == nil && u.ID != "" {
id = u.ID
}
} else if r.Msg.User != nil {
nick, id = r.Msg.User.Name, r.Msg.User.ID
}
nick = strings.ToLower(nick)
log.Debug().Msgf("resolveUser(%s, %s) => (%s, %s)", r.Msg.User.ID, nick, nick, id)
return nick, id
}