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"
2022-07-19 01:44:03 +00:00
"github.com/rs/zerolog/log"
"github.com/velour/catbase/config"
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"
2022-07-19 01:44:03 +00:00
"sync"
2019-03-07 16:35:42 +00:00
2016-03-19 18:02:46 +00:00
"github.com/jmoiron/sqlx"
2020-05-25 18:05:21 +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 {
2022-07-19 01:44:03 +00:00
b bot . Bot
db * sqlx . DB
cfg * config . Config
modify sync . Mutex
2013-01-23 21:25:04 +00:00
}
type Item struct {
2016-03-19 18:02:46 +00:00
* sqlx . DB
2021-02-14 21:57:22 +00:00
ID int64
Nick string
Item string
2024-06-19 13:13:12 +00:00
Count int64
2022-07-17 17:48:12 +00:00
UserID sql . NullString
2013-01-23 21:25:04 +00:00
}
2018-01-04 17:23:59 +00:00
type alias struct {
* sqlx . DB
ID int64
Item string
PointsTo string ` db:"points_to" `
}
2019-05-27 18:27:34 +00:00
// GetItems returns all counters
2024-02-27 20:36:50 +00:00
func GetAllItemsByUser ( db * sqlx . DB ) ( map [ string ] [ ] Item , error ) {
2019-05-27 18:27:34 +00:00
var items [ ] Item
err := db . Select ( & items , ` select * from counter ` )
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 {
items [ i ] . DB = db
}
2024-02-27 20:36:50 +00:00
out := map [ string ] [ ] Item { }
for _ , it := range items {
out [ it . Nick ] = append ( out [ it . Nick ] , it )
}
return out , nil
2019-05-27 18:27:34 +00:00
}
2016-03-19 18:27:02 +00:00
// GetItems returns all counters for a subject
2021-02-14 21:57:22 +00:00
func GetItems ( db * sqlx . DB , 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
if id != "" {
err = db . Select ( & items , ` select * from counter where userid = ? ` , id )
} else {
err = db . Select ( & items , ` select * from counter where nick = ? ` , nick )
}
2016-03-19 18:02:46 +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
2017-01-24 02:13:21 +00:00
for i := range items {
items [ i ] . DB = db
2016-03-19 18:02:46 +00:00
}
return items , nil
}
2018-01-04 12:39:24 +00:00
func LeaderAll ( db * sqlx . DB ) ( [ ] Item , error ) {
2020-01-24 21:32:24 +00:00
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 `
2018-01-04 12:39:24 +00:00
var items [ ] Item
err := db . Select ( & items , s )
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 {
items [ i ] . DB = db
}
return items , nil
}
func Leader ( db * sqlx . DB , itemName string ) ( [ ] Item , error ) {
2018-01-04 17:23:59 +00:00
itemName = strings . ToLower ( itemName )
2018-01-04 12:39:24 +00:00
s := ` select * from counter where item=? order by count desc `
var items [ ] Item
err := db . Select ( & items , s , itemName )
if err != nil {
return nil , err
}
for i := range items {
items [ i ] . DB = db
}
return items , nil
}
2020-04-20 10:20:21 +00:00
func RmAlias ( db * sqlx . DB , aliasName string ) error {
q := ` delete from counter_alias where item = ? `
tx , err := db . Beginx ( )
if err != nil {
return err
}
_ , err = tx . Exec ( q , aliasName )
if err != nil {
if err := tx . Rollback ( ) ; err != nil {
return err
}
return err
}
err = tx . Commit ( )
return err
}
func MkAlias ( db * sqlx . DB , aliasName , pointsTo string ) ( * alias , error ) {
2022-07-19 01:44:03 +00:00
aliasName = strings . TrimSpace ( strings . ToLower ( aliasName ) )
pointsTo = strings . TrimSpace ( strings . ToLower ( pointsTo ) )
2018-01-04 17:23:59 +00:00
res , err := db . Exec ( ` insert into counter_alias (item, points_to) values (?, ?) ` ,
2020-04-20 10:20:21 +00:00
aliasName , pointsTo )
2018-01-04 17:23:59 +00:00
if err != nil {
2020-04-20 10:20:21 +00:00
_ , err := db . Exec ( ` update counter_alias set points_to=? where item=? ` , pointsTo , aliasName )
2018-01-04 17:23:59 +00:00
if err != nil {
return nil , err
}
var a alias
2020-04-20 10:20:21 +00:00
if err := db . Get ( & a , ` select * from counter_alias where item=? ` , aliasName ) ; err != nil {
2018-01-04 17:23:59 +00:00
return nil , err
}
return & a , nil
}
id , _ := res . LastInsertId ( )
2020-04-20 10:20:21 +00:00
return & alias { db , id , aliasName , pointsTo } , nil
2018-01-04 17:23:59 +00:00
}
2020-05-26 15:38:55 +00:00
// GetUserItem returns a specific counter for all subjects
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
2021-02-14 21:57:22 +00:00
func GetUserItem ( db * sqlx . DB , nick , id , itemName string ) ( Item , error ) {
2019-11-25 19:26:26 +00:00
itemName = trimUnicode ( itemName )
2016-03-19 18:02:46 +00:00
var item Item
item . DB = db
2018-01-04 17:23:59 +00:00
var a alias
if err := db . Get ( & a , ` select * from counter_alias where item=? ` , itemName ) ; err == nil {
itemName = a . PointsTo
} else {
2019-03-07 16:35:42 +00:00
log . Error ( ) . Err ( err ) . Interface ( "alias" , a )
2018-01-04 17:23:59 +00:00
}
2021-02-14 21:57:22 +00:00
var err error
if id != "" {
err = db . Get ( & item , ` select * from counter where userid = ? and item= ? ` , id , itemName )
} else {
err = db . Get ( & item , ` select * from counter where nick = ? and item= ? ` , nick , itemName )
}
2016-03-19 18:02:46 +00:00
switch err {
case sql . ErrNoRows :
item . ID = - 1
item . Nick = nick
item . Item = itemName
2022-07-17 18:43:41 +00:00
item . UserID . Scan ( id )
2016-03-19 18:02:46 +00:00
case nil :
default :
return Item { } , err
}
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-02-14 21:57:22 +00:00
res , err := i . Exec ( ` insert into counter (nick, item, count, userid) values (?, ?, ?, ?); ` ,
2022-07-17 18:43:41 +00:00
i . Nick , i . Item , i . Count , i . UserID . String )
2019-01-20 20:21:26 +00:00
if err != nil {
return err
}
2016-03-19 18:02:46 +00:00
id , _ := res . LastInsertId ( )
// hackhackhack?
i . ID = id
return err
}
2016-03-19 18:27:02 +00:00
// UpdateDelta sets a value
// This will create or delete the item if necessary
2024-06-19 13:13:12 +00:00
func ( i * Item ) Update ( r * bot . Request , value int64 ) error {
2016-03-19 18:27:02 +00:00
i . Count = value
2016-03-19 18:02:46 +00:00
if i . Count == 0 && i . ID != - 1 {
return i . Delete ( )
}
if i . ID == - 1 {
2022-07-17 18:43:41 +00:00
if err := i . Create ( ) ; err != nil {
return err
}
2016-03-19 18:02:46 +00:00
}
2019-03-07 16:35:42 +00:00
log . Debug ( ) .
Interface ( "i" , i ) .
2024-06-19 13:13:12 +00:00
Int64 ( "value" , value ) .
2019-03-07 16:35:42 +00:00
Msg ( "Updating item" )
2016-03-19 18:02:46 +00:00
_ , err := i . Exec ( ` update counter set count = ? where id = ? ` , i . Count , i . ID )
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
2024-06-19 13:13:12 +00:00
func ( i * Item ) UpdateDelta ( r * bot . Request , delta int64 ) 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 {
_ , err := i . Exec ( ` delete from counter where id = ? ` , i . ID )
i . ID = - 1
return err
}
2022-07-23 04:17:44 +00:00
func ( p * CounterPlugin ) migrate ( r bot . Request ) ( retVal bool ) {
2021-07-21 18:52:45 +00:00
db := p . db
2021-02-14 21:57:22 +00:00
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" )
2022-07-23 04:17:44 +00:00
return
2021-02-14 21:57:22 +00:00
}
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" )
}
2022-07-23 04:17:44 +00:00
return
2021-02-14 21:57:22 +00:00
}
func setupDB ( b bot . Bot ) error {
db := b . DB ( )
db . MustExec ( ` create table if not exists counter (
2016-01-15 06:12:26 +00:00
id integer primary key ,
nick string ,
item string ,
2022-07-17 18:43:41 +00:00
count integer ,
userid string
2019-01-21 17:36:02 +00:00
) ; ` )
2021-02-14 21:57:22 +00:00
db . MustExec ( ` create table if not exists counter_alias (
2018-01-04 17:23:59 +00:00
id integer PRIMARY KEY AUTOINCREMENT ,
item string NOT NULL UNIQUE ,
points_to string NOT NULL
2019-01-21 17:36:02 +00:00
) ; ` )
2021-02-14 21:57:22 +00:00
2022-07-17 18:43:41 +00:00
tx := db . MustBegin ( )
2021-02-14 21:57:22 +00:00
count := 0
err := tx . Get ( & count , ` SELECT count(*) FROM pragma_table_info('counter') where name='userid' ` )
if err != nil {
return err
}
if count == 0 {
tx . MustExec ( ` alter table counter add column userid string ` )
}
tx . Commit ( )
return nil
}
// NewCounterPlugin creates a new CounterPlugin with the Plugin interface
func New ( b bot . Bot ) * CounterPlugin {
if err := setupDB ( b ) ; err != nil {
panic ( err )
}
2019-02-05 19:41:38 +00:00
cp := & CounterPlugin {
2021-07-28 15:32:59 +00:00
b : b ,
db : b . DB ( ) ,
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-07-21 18:52:45 +00:00
if _ , err := MkAlias ( p . db , what , to ) ; 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-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-07-21 18:52:45 +00:00
if err := RmAlias ( p . db , 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-07-21 18:52:45 +00:00
cmd = func ( ) ( [ ] Item , error ) { return LeaderAll ( p . db ) }
2021-02-01 02:21:19 +00:00
} else {
itNameTxt = fmt . Sprintf ( " for %s" , what )
2021-07-21 18:52:45 +00:00
cmd = func ( ) ( [ ] Item , error ) { return Leader ( p . db , 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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
items , err := GetItems ( p . db , 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" )
2024-06-19 13:13:12 +00:00
// pull all the items associated with "subject"
2021-07-21 18:52:45 +00:00
items , err := GetItems ( p . db , 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-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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
it , err := GetUserItem ( p . db , 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-02-01 02:21:19 +00:00
var item Item
2021-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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
}
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 ) )
2022-07-17 18:43:41 +00:00
err = item . UpdateDelta ( & r , 1 )
if err != nil {
log . Error ( ) . Err ( err ) . Msgf ( "Could not UpdateDelta" )
}
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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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
}
2024-06-19 13:13:12 +00:00
n , _ := strconv . ParseInt ( r . Values [ "amount" ] , 10 , 64 )
2021-02-01 02:21:19 +00:00
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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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
}
2024-06-19 13:13:12 +00:00
n , _ := strconv . ParseInt ( r . Values [ "amount" ] , 10 , 64 )
2021-02-01 02:21:19 +00:00
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.
2022-03-22 01:32:44 +00:00
func ( p * CounterPlugin ) help ( c bot . Connector , kind bot . Kind , message msg . Message , args ... any ) 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 {
2022-07-19 01:44:03 +00:00
p . modify . Lock ( )
defer p . modify . Unlock ( )
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-07-21 18:52:45 +00:00
item , err := GetUserItem ( p . db , 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 )
2024-06-19 13:13:12 +00:00
var delta int64 = 1
2020-01-21 21:26:06 +00:00
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
2024-06-19 13:13:12 +00:00
func sendUpdate ( r * bot . Request , who , what string , amount int64 ) {
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
}