// © 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" bh "github.com/timshannon/bolthold" "math/rand" "regexp" "strings" "github.com/rs/zerolog/log" "github.com/velour/catbase/bot" "github.com/velour/catbase/bot/msg" "github.com/velour/catbase/config" ) type InventoryPlugin struct { *bh.Store bot bot.Bot config *config.Config handlers bot.HandlerTable } // New creates a new InventoryPlugin with the Plugin interface func New(b bot.Bot) *InventoryPlugin { config := b.Config() p := &InventoryPlugin{ Store: b.Store(), bot: b, config: config, } b.RegisterFilter("$item", p.itemFilter) b.RegisterFilter("$giveitem", p.giveItemFilter) p.register() return p } type Item struct { Name string `boltholdKey:"Name"` } 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) register() { nick := p.config.Get("nick", "bot") p.handlers = append(p.handlers, bot.HandlerSpec{Kind: bot.Message, IsCmd: true, Regex: regexp.MustCompile(`inventory`), Handler: func(r bot.Request) bool { 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(r.Conn, bot.Message, r.Msg.Channel, say) return true }, }) h := func(r bot.Request) bool { log.Debug().Msgf("Adding item: %s", r.Values["item"]) return p.addItem(r.Conn, r.Msg, r.Values["item"]) } for _, r := range []*regexp.Regexp{ regexp.MustCompile(`(?i)^take this (?P.+)$`), regexp.MustCompile(`(?i)^have a (?P.+)$`), } { p.handlers = append(p.handlers, bot.HandlerSpec{Kind: bot.Message, IsCmd: false, Regex: r, Handler: h}) } for _, r := range []*regexp.Regexp{ regexp.MustCompile(fmt.Sprintf(`(?i)^puts (?P\S+) in %s$`, nick)), regexp.MustCompile(fmt.Sprintf("(?i)^gives %s (?P.+)$", nick)), regexp.MustCompile(fmt.Sprintf(`(?i)^gives (?P\S+) to %s$`, nick)), } { p.handlers = append(p.handlers, bot.HandlerSpec{Kind: bot.Action, IsCmd: false, Regex: r, Handler: h}) } p.bot.RegisterTable(p, p.handlers) } func (p *InventoryPlugin) removeRandom() string { items := []Item{} err := p.Find(&items, &bh.Query{}) if err != nil { log.Error().Err(err).Msgf("Error finding random entry") return "IAMERROR" } item := items[rand.Intn(len(items))] err = p.Delete(item.Name, Item{}) return item.Name } func (p *InventoryPlugin) count() int { count, err := p.Store.Count(Item{}, &bh.Query{}) if err != nil { log.Error().Err(err).Msg("Error checking for item") return -1 } return count } func (p *InventoryPlugin) random() string { items := []Item{} err := p.Find(&items, &bh.Query{}) if err != nil { log.Error().Err(err).Msgf("Error finding random entry") return "IAMERROR" } item := items[rand.Intn(len(items))] return item.Name } func (p *InventoryPlugin) getAll() []string { items := []Item{} err := p.Find(&items, &bh.Query{}) if err != nil { log.Error().Err(err).Msg("Error getting all items") return []string{} } output := []string{} for _, item := range items { output = append(output, item.Name) } return output } func (p *InventoryPlugin) exists(i string) bool { count, err := p.Store.Count(Item{}, bh.Where("Item").Eq(i)) if err != nil { log.Error().Err(err).Msg("Error checking for item") return false } return count > 0 } func (p *InventoryPlugin) remove(i string) { err := p.Store.Delete(i, Item{}) if err != nil { log.Error().Msg("Error inserting new inventory item") } } func (p *InventoryPlugin) addItem(c bot.Connector, m msg.Message, i string) bool { if p.exists(i) { p.bot.Send(c, 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.Store.Insert(i, Item{Name: i}) if err != nil { log.Error().Err(err).Msg("Error inserting new inventory item") } if removed != "" { p.bot.Send(c, bot.Action, m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name)) } else { p.bot.Send(c, bot.Action, m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name)) } return true }