catbase/plugins/inventory/inventory.go

228 lines
6.0 KiB
Go

// © 2016 the CatBase Authors under the WTFPL license. See AUTHORS for the list of authors.
// Package inventory contains the plugin that allows the bot to pad messages
// See the bucket RFC inventory: http://wiki.xkcd.com/irc/Bucket_inventory
package inventory
import (
"fmt"
"regexp"
"strings"
"github.com/rs/zerolog/log"
"github.com/jmoiron/sqlx"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/config"
)
type InventoryPlugin struct {
*sqlx.DB
bot bot.Bot
config *config.Config
r1, r2, r3, r4, r5 *regexp.Regexp
}
// New creates a new InventoryPlugin with the Plugin interface
func New(b bot.Bot) *InventoryPlugin {
config := b.Config()
nick := config.Get("nick", "bot")
r1, err := regexp.Compile("take this (.+)")
checkerr(err)
r2, err := regexp.Compile("have a (.+)")
checkerr(err)
r3, err := regexp.Compile(fmt.Sprintf("puts (.+) in %s([^a-zA-Z].*)?", nick))
checkerr(err)
r4, err := regexp.Compile(fmt.Sprintf("gives %s (.+)", nick))
checkerr(err)
r5, err := regexp.Compile(fmt.Sprintf("gives (.+) to %s([^a-zA-Z].*)?", nick))
checkerr(err)
p := &InventoryPlugin{
DB: b.DB(),
bot: b,
config: config,
r1: r1, r2: r2, r3: r3, r4: r4, r5: r5,
}
b.RegisterFilter("$item", p.itemFilter)
b.RegisterFilter("$giveitem", p.giveItemFilter)
_, err = p.DB.Exec(`create table if not exists inventory (
item string primary key
);`)
if err != nil {
log.Fatal().Err(err)
}
b.Register(p, bot.Message, p.message)
return p
}
func (p *InventoryPlugin) giveItemFilter(input string) string {
for strings.Contains(input, "$giveitem") {
item := p.random()
input = strings.Replace(input, "$giveitem", item, 1)
p.remove(item)
}
return input
}
func (p *InventoryPlugin) itemFilter(input string) string {
for strings.Contains(input, "$item") {
input = strings.Replace(input, "$item", p.random(), 1)
}
return input
}
func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool {
m := message.Body
log.Debug().Msgf("inventory trying to read %+v", message)
if message.Command {
if strings.ToLower(m) == "inventory" {
items := p.getAll()
say := "I'm not holding anything"
if len(items) > 0 {
log.Debug().Msgf("I think I have more than 0 items: %+v, len(items)=%d", items, len(items))
say = fmt.Sprintf("I'm currently holding %s", strings.Join(items, ", "))
}
p.bot.Send(bot.Message, message.Channel, say)
return true
}
// <Randall> Bucket[:,] take this (.+)
// <Randall> Bucket[:,] have a (.+)
if matches := p.r1.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1])
}
if matches := p.r2.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1])
}
}
if message.Action {
log.Debug().Msg("Inventory found an action")
// * Randall puts (.+) in Bucket([^a-zA-Z].*)?
// * Randall gives Bucket (.+)
// * Randall gives (.+) to Bucket([^a-zA-Z].*)?
if matches := p.r3.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1])
}
if matches := p.r4.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1])
}
if matches := p.r5.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1])
}
}
return false
}
func (p *InventoryPlugin) removeRandom() string {
var name string
err := p.QueryRow(`select item from inventory order by random() limit 1`).Scan(
&name,
)
if err != nil {
log.Error().Err(err).Msgf("Error finding random entry")
return "IAMERROR"
}
_, err = p.Exec(`delete from inventory where item=?`, name)
if err != nil {
log.Error().Err(err).Msgf("Error finding random entry")
return "IAMERROR"
}
return name
}
func (p *InventoryPlugin) count() int {
var output int
err := p.QueryRow(`select count(*) as count from inventory`).Scan(&output)
if err != nil {
log.Error().Err(err).Msg("Error checking for item")
return -1
}
return output
}
func (p *InventoryPlugin) random() string {
var name string
err := p.QueryRow(`select item from inventory order by random() limit 1`).Scan(
&name,
)
if err != nil {
log.Error().Err(err).Msg("Error finding random entry")
return "IAMERROR"
}
return name
}
func (p *InventoryPlugin) getAll() []string {
rows, err := p.Queryx(`select item from inventory`)
if err != nil {
log.Error().Err(err).Msg("Error getting all items")
return []string{}
}
output := []string{}
for rows.Next() {
var item string
rows.Scan(&item)
output = append(output, item)
}
rows.Close()
return output
}
func (p *InventoryPlugin) exists(i string) bool {
var output int
err := p.QueryRow(`select count(*) as count from inventory where item=?`, i).Scan(&output)
if err != nil {
log.Error().Err(err).Msg("Error checking for item")
return false
}
return output > 0
}
func (p *InventoryPlugin) remove(i string) {
_, err := p.Exec(`delete from inventory where item=?`, i)
if err != nil {
log.Error().Msg("Error inserting new inventory item")
}
}
func (p *InventoryPlugin) addItem(m msg.Message, i string) bool {
if p.exists(i) {
p.bot.Send(bot.Message, m.Channel, fmt.Sprintf("I already have %s.", i))
return true
}
var removed string
max := p.config.GetInt("inventory.max", 10)
if p.count() > max {
removed = p.removeRandom()
}
_, err := p.Exec(`INSERT INTO inventory (item) values (?)`, i)
if err != nil {
log.Error().Err(err).Msg("Error inserting new inventory item")
}
if removed != "" {
p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name))
} else {
p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name))
}
return true
}
func checkerr(e error) {
if e != nil {
log.Error().Err(e)
}
}