2016-03-19 15:44:27 +00:00
package counter
2013-01-23 21:25:04 +00:00
import (
2016-01-15 06:12:26 +00:00
"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"
2017-02-15 04:56:29 +00:00
"regexp"
2017-01-23 15:10:54 +00:00
"strconv"
2013-01-23 21:25:04 +00:00
"strings"
2016-01-15 06:12:26 +00:00
2019-03-07 16:35:42 +00:00
"github.com/rs/zerolog/log"
2021-07-21 18:52:45 +00:00
"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"
2016-04-01 14:20:03 +00:00
"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" `
2021-02-14 21:57:22 +00:00
Nick string
Item string
Count int
UserID string
2013-01-23 21:25:04 +00:00
}
2021-12-22 03:36:08 +00:00
type Alias 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
}
2021-07-21 18:52:45 +00:00
// 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-20 17:40:10 +00:00
func GetItems ( store * bh . Store , nick , id string ) ( [ ] Item , error ) {
2016-03-19 18:02:46 +00:00
var items [ ] Item
2021-02-14 21:57:22 +00:00
var err error
2021-12-20 17:40:10 +00:00
q := & bh . Query { }
2021-02-14 21:57:22 +00:00
if id != "" {
2021-12-21 04:31:19 +00:00
q = bh . Where ( "UserID" ) . Eq ( id )
2021-02-14 21:57:22 +00:00
} else {
2021-12-21 04:31:19 +00:00
q = bh . Where ( "Nick" ) . Eq ( nick )
2021-02-14 21:57:22 +00:00
}
2021-12-20 17:40:10 +00:00
if err = store . Find ( & items , q ) ; err != nil {
2016-03-19 18:02:46 +00:00
return nil , err
}
2021-07-21 18:52:45 +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 {
2020-03-26 18:00:08 +00:00
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-22 03:36:08 +00:00
a := & Alias { }
2021-12-21 04:31:19 +00:00
if err := store . FindOne ( a , bh . Where ( "Item" ) . Eq ( aliasName ) ) ; err != nil {
return err
}
2021-12-22 03:36:08 +00:00
return store . Delete ( a . ID , Alias { } )
2020-04-20 10:20:21 +00:00
}
2021-12-22 03:36:08 +00:00
func MkAlias ( store * bh . Store , aliasName , pointsTo string ) ( * Alias , 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-22 03:36:08 +00:00
alias := & Alias {
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-22 03:36:08 +00:00
var a Alias
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-22 03:36:08 +00:00
var a Alias
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
}
2021-02-14 21:57:22 +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 ) .
2021-02-14 21:57:22 +00:00
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
}
2021-02-14 21:57:22 +00:00
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")
//}
2021-02-14 21:57:22 +00:00
return false
}
// NewCounterPlugin creates a new CounterPlugin with the Plugin interface
func New ( b bot . Bot ) * CounterPlugin {
2019-02-05 19:41:38 +00:00
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
2021-02-14 21:57:22 +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 )
2019-02-05 19:41:38 +00:00
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 )
} )
2019-02-05 19:41:38 +00:00
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 == "" {
2021-07-21 18:52:45 +00:00
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" )
2021-07-21 18:52:45 +00:00
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 )
2021-07-21 18:52:45 +00:00
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 == "" {
2021-07-21 18:52:45 +00:00
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" )
2021-07-21 18:52:45 +00:00
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
}
2021-07-21 18:52:45 +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 {
2021-07-21 18:52:45 +00:00
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 ,
)
}
2021-07-21 18:52:45 +00:00
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 {
2021-02-14 21:57:22 +00:00
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" )
2021-07-21 18:52:45 +00:00
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 ( )
}
2021-07-21 18:52:45 +00:00
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
}
2013-01-23 21:40:51 +00:00
2021-02-01 02:21:19 +00:00
func ( p * CounterPlugin ) inspectCmd ( r bot . Request ) bool {
2021-02-14 21:57:22 +00:00
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 {
2021-02-14 21:57:22 +00:00
nick , id = p . resolveUser ( r , who )
2021-02-01 02:21:19 +00:00
}
2021-02-14 21:57:22 +00:00
channel := r . Msg . Channel
c := r . Conn
2021-02-01 02:21:19 +00:00
log . Debug ( ) .
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
Str ( "nick" , nick ) .
Str ( "id" , id ) .
2021-02-01 02:21:19 +00:00
Msg ( "Error retrieving items" )
2021-07-21 18:52:45 +00:00
p . b . Send ( c , bot . Message , channel , "Something went wrong finding that counter;" )
2021-02-01 02:21:19 +00:00
return true
}
2013-01-23 21:45:37 +00:00
2021-12-21 04:31:19 +00:00
log . Debug ( ) . Msgf ( "items: %v" , items )
2021-02-14 21:57:22 +00:00
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 += ","
2013-01-23 21:40:51 +00:00
}
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
2016-01-15 06:12:26 +00:00
}
2021-02-01 02:21:19 +00:00
}
resp += "."
2016-01-15 06:12:26 +00:00
2021-02-01 02:21:19 +00:00
if count == 0 {
2021-07-21 18:52:45 +00:00
p . b . Send ( c , bot . Message , channel , fmt . Sprintf ( "%s has no counters." , nick ) )
2013-01-23 21:40:51 +00:00
return true
2021-02-01 02:21:19 +00:00
}
2013-01-23 21:40:51 +00:00
2021-07-21 18:52:45 +00:00
p . b . Send ( c , bot . Message , channel , resp )
2021-02-01 02:21:19 +00:00
return true
}
2013-01-23 21:40:51 +00:00
2021-02-01 02:21:19 +00:00
func ( p * CounterPlugin ) clearCmd ( r bot . Request ) bool {
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
Str ( "nick" , nick ) .
Str ( "id" , id ) .
2021-02-01 02:21:19 +00:00
Str ( "itemName" , itemName ) .
Msg ( "Error getting item to remove" )
2021-07-21 18:52:45 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
Str ( "nick" , nick ) .
Str ( "id" , id ) .
2021-02-01 02:21:19 +00:00
Str ( "itemName" , itemName ) .
Msg ( "Error removing item" )
2021-07-21 18:52:45 +00:00
p . b . Send ( c , bot . Message , channel , "Something went wrong removing that counter;" )
2013-01-23 21:40:51 +00:00
return true
2021-02-01 02:21:19 +00:00
}
2013-01-23 21:40:51 +00:00
2021-07-21 18:52:45 +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 {
2021-02-14 21:57:22 +00:00
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 {
2021-02-14 21:57:22 +00:00
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 :
2021-07-21 18:52:45 +00:00
p . b . Send ( r . Conn , bot . Message , r . Msg . Channel , fmt . Sprintf ( "I don't think %s has any %s." ,
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
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
2021-07-21 18:52:45 +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 {
2021-02-14 21:57:22 +00:00
nick , id := r . Msg . User . Name , r . Msg . User . ID
2021-02-01 02:21:19 +00:00
if r . Values [ "who" ] != "" {
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
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 {
2021-02-14 21:57:22 +00:00
nick , id := r . Msg . User . Name , r . Msg . User . ID
2021-02-01 02:21:19 +00:00
if r . Values [ "who" ] != "" {
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
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
}
2021-07-21 18:52:45 +00:00
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 {
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
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 )
2021-07-21 18:52:45 +00:00
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 {
2021-02-14 21:57:22 +00:00
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 ) .
2021-02-14 21:57:22 +00:00
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 )
2021-07-21 18:52:45 +00:00
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 {
2021-07-21 18:52:45 +00:00
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`." )
2021-07-21 18:52:45 +00:00
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>`" )
2019-02-05 19:41:38 +00:00
return true
2013-05-08 00:08:18 +00:00
}
2013-06-01 17:10:15 +00:00
2021-02-01 02:21:19 +00:00
func ( p * CounterPlugin ) teaMatchCmd ( r bot . Request ) bool {
2021-02-14 21:57:22 +00:00
nick , id := r . Msg . User . Name , r . Msg . User . ID
2021-02-01 02:21:19 +00:00
channel := r . Msg . Channel
2018-10-19 18:20:40 +00:00
2021-02-01 02:21:19 +00:00
submatches := teaRegex . FindStringSubmatch ( r . Msg . Body )
2018-10-19 18:20:40 +00:00
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 )
2018-10-19 18:20:40 +00:00
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" )
2018-10-19 18:20:40 +00:00
// 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 )
2020-01-21 21:26:06 +00:00
delta := 1
if item . Count < 0 {
delta = - 1
}
2021-07-21 18:52:45 +00:00
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 )
2018-10-19 18:20:40 +00:00
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 ) {
2021-11-28 19:12:38 +00:00
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
}
}
2021-02-14 21:57:22 +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
}