Compare commits

...

3 Commits

Author SHA1 Message Date
930839ab58
Prevent dice overflow
rand.Int() would panic when the max value is <= 0, which happens when
big.NewInt() was fed with a too large number. Avoid this by validating
the big.NewInt() return beforehand. Add error handling to all callers to
both gracefully return to IRC and to log an error message.
Rename the shadowed "max" variable whilst at it to avoid confusion.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
2024-10-10 18:43:46 +02:00
91b0e21b7a Merge pull request 'Configurable database path' (#26) from config into master
Reviewed-on: #26
2024-10-10 16:56:19 +02:00
dfe7deff72
Configurable database path
Allow the database file to reside in a user defined location instead of
requiring it to be in the working directory.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
2024-10-10 00:04:16 +02:00
6 changed files with 117 additions and 24 deletions

View File

@ -1,4 +1,5 @@
watbot: watbot:
database: wat.db # wat.db (in the working directory) is the default
server: server:
host: irc.casa # mandatory, no default host: irc.casa # mandatory, no default
port: 6697 port: 6697

View File

@ -20,6 +20,7 @@ type Config struct {
} }
type watConfig struct { type watConfig struct {
Database string `default:"wat.db" yaml:"database"`
Nick string `yaml:"nick"` Nick string `yaml:"nick"`
Pass string `yaml:"pass"` Pass string `yaml:"pass"`
User string `yaml:"user"` User string `yaml:"user"`
@ -99,6 +100,7 @@ func main() {
Name: config.Name, Name: config.Name,
} }
watConfig := wat.WatConfig{ watConfig := wat.WatConfig{
DatabasePath: config.Database,
AutoJoinChannels: config.Channels.Join, AutoJoinChannels: config.Channels.Join,
PermittedChannels: config.Channels.Permitted, PermittedChannels: config.Channels.Permitted,
IgnoredHosts: config.Ignores.Hosts, IgnoredHosts: config.Ignores.Hosts,

View File

@ -20,6 +20,7 @@ type WatBot struct {
} }
type WatConfig struct { type WatConfig struct {
DatabasePath string
BotHosts []string BotHosts []string
BotGames BotGameConfig BotGames BotGameConfig
AdminHosts []string AdminHosts []string
@ -30,7 +31,7 @@ type WatConfig struct {
func NewWatBot(config *irc.ClientConfig, watConfig *WatConfig, serverConn *tls.Conn) *WatBot { func NewWatBot(config *irc.ClientConfig, watConfig *WatConfig, serverConn *tls.Conn) *WatBot {
wat := WatBot{conn: serverConn, Nick: config.Nick, c: watConfig} wat := WatBot{conn: serverConn, Nick: config.Nick, c: watConfig}
wat.Db = NewWatDb() wat.Db = NewWatDb(watConfig.DatabasePath)
wat.game = NewWatGame(&wat, wat.Db) wat.game = NewWatGame(&wat, wat.Db)
wat.integration = NewWatIntegration(&wat, wat.Db, &WatIntegrationConfig{BotHosts: watConfig.BotHosts, BotGames: watConfig.BotGames}) wat.integration = NewWatIntegration(&wat, wat.Db, &WatIntegrationConfig{BotHosts: watConfig.BotHosts, BotGames: watConfig.BotGames})
config.Handler = irc.HandlerFunc(wat.HandleIrcMsg) config.Handler = irc.HandlerFunc(wat.HandleIrcMsg)

View File

@ -52,10 +52,10 @@ type WatDb struct {
db *gorm.DB db *gorm.DB
} }
func NewWatDb() *WatDb { func NewWatDb(dbpath string) *WatDb {
w := WatDb{} w := WatDb{}
var err error var err error
w.db, err = gorm.Open(sqlite.Open("wat.db"), &gorm.Config{}) w.db, err = gorm.Open(sqlite.Open(dbpath), &gorm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -121,9 +121,17 @@ func (g *WatGame) help() string {
return ret return ret
} }
func (g *WatGame) RandInt(max int64) uint64 { func (g *WatGame) RandInt(maxx int64) (uint64, error) {
i, _ := rand.Int(rand.Reader, big.NewInt(max)) bi := big.NewInt(maxx)
return i.Uint64() // prevent panic of rand.Int on big numbers
if bi.BitLen() < 2 {
return 0, fmt.Errorf("overflow, got %d", bi)
}
i, err := rand.Int(rand.Reader, bi)
if err != nil {
return 0, err
}
return i.Uint64(), nil
} }
func (g *WatGame) Heal(player *Player, fields []string) string { func (g *WatGame) Heal(player *Player, fields []string) string {
@ -166,8 +174,11 @@ func (g *WatGame) Dice(player *Player, fields []string) string {
roll = i roll = i
} }
} }
answer := g.RandInt(int64(roll)) + 1 answer, err := g.RandInt(int64(roll))
return fmt.Sprintf("1d%d - %d", roll, answer) if e := handleError(err); e != "" {
return e
}
return fmt.Sprintf("1d%d - %d", roll + 1, answer)
} }
type PositiveError struct{} type PositiveError struct{}
@ -206,12 +217,20 @@ func (g *WatGame) Roll(player *Player, fields []string) string {
} }
lotteryNum := int64(-1) lotteryNum := int64(-1)
if dieSize > 100 { if dieSize > 100 {
lotteryNum = int64(g.RandInt(dieSize)) + 1 lotteryNumRand, randErr := g.RandInt(dieSize)
if e := handleError(randErr); e != "" {
return e
}
lotteryNum = int64(lotteryNumRand) + 1
} }
if amount > player.Coins { if amount > player.Coins {
return "wat? brokeass" return "wat? brokeass"
} }
n := int64(g.RandInt(dieSize)) + 1 nRand, randErr := g.RandInt(dieSize)
if e := handleError(randErr); e != "" {
return e
}
n := int64(nRand) + 1
ret := fmt.Sprintf("%s rolls the %d sided die... %d! ", player.Nick, dieSize, n) ret := fmt.Sprintf("%s rolls the %d sided die... %d! ", player.Nick, dieSize, n)
if n == lotteryNum { if n == lotteryNum {
player.Coins += player.Coins player.Coins += player.Coins
@ -255,11 +274,18 @@ func (g *WatGame) Punch(player *Player, fields []string) string {
if !target.Conscious() { if !target.Conscious() {
return "wat? you're punching someone who is already unconscious. u crazy?" return "wat? you're punching someone who is already unconscious. u crazy?"
} }
chance := g.RandInt(6) + 1 chance, randErr := g.RandInt(6)
dmg := g.RandInt(6) + 1 if e := handleError(randErr); e != "" {
return e
}
dmg, randErr := g.RandInt(6)
if e := handleError(randErr); e != "" {
return e
}
dmg = dmg + 1
ret := fmt.Sprintf("%s rolls a d6... %s ", player.Nick, player.Nick) ret := fmt.Sprintf("%s rolls a d6... %s ", player.Nick, player.Nick)
dmg += uint64(player.Level(player.Anarchy)) dmg += uint64(player.Level(player.Anarchy))
if chance > 3 { if chance + 1 > 3 {
ret += fmt.Sprintf("hits %s for %d points of damage! ", target.Nick, dmg) ret += fmt.Sprintf("hits %s for %d points of damage! ", target.Nick, dmg)
target.Health -= int64(dmg) target.Health -= int64(dmg)
g.db.Update(target) g.db.Update(target)
@ -300,7 +326,11 @@ func (g *WatGame) Frame(player *Player, fields []string) string {
if target.Coins < amount { if target.Coins < amount {
return fmt.Sprintf("wat? %s is too poor for this.", target.Nick) return fmt.Sprintf("wat? %s is too poor for this.", target.Nick)
} }
n := g.RandInt(6) + 1 n, randErr := g.RandInt(6)
if e := handleError(randErr); e != "" {
return e
}
n = n + 1
ret := fmt.Sprintf("%s rolls a d6 to frame %s with %d %s: It's a %d! (<3 wins). ", player.Nick, target.Nick, amount, currency, n) ret := fmt.Sprintf("%s rolls a d6 to frame %s with %d %s: It's a %d! (<3 wins). ", player.Nick, target.Nick, amount, currency, n)
if n < 3 { if n < 3 {
ret += fmt.Sprintf("You frame %s for a minor crime. They pay me %d.", target.Nick, amount) ret += fmt.Sprintf("You frame %s for a minor crime. They pay me %d.", target.Nick, amount)
@ -335,7 +365,11 @@ func (g *WatGame) Steal(player *Player, fields []string) string {
if target.Coins < amount { if target.Coins < amount {
return fmt.Sprintf("wat? %s is poor and doesn't have that much to steal. (%d %s)", target.Nick, target.Coins, currency) return fmt.Sprintf("wat? %s is poor and doesn't have that much to steal. (%d %s)", target.Nick, target.Coins, currency)
} }
n := g.RandInt(6) + 1 n, randErr := g.RandInt(6)
if e := handleError(randErr); e != "" {
return e
}
n = n + 1
ret := fmt.Sprintf("%s is trying to steal %d %s from %s... ", player.Nick, amount, currency, target.Nick) ret := fmt.Sprintf("%s is trying to steal %d %s from %s... ", player.Nick, amount, currency, target.Nick)
if n < 3 { if n < 3 {
ret += "You did it! Sneaky bastard!" ret += "You did it! Sneaky bastard!"
@ -380,7 +414,11 @@ func (g *WatGame) Leech(player *Player, fields []string) string {
if err != "" { if err != "" {
return err return err
} }
r := g.RandInt(10) + 1 r, randErr := g.RandInt(10)
if e := handleError(randErr); e != "" {
return e
}
r = r + 1
reply := fmt.Sprintf("You muster your wealth and feed it to %s. ", g.bot.Nick) reply := fmt.Sprintf("You muster your wealth and feed it to %s. ", g.bot.Nick)
hpDown := amount / divisor hpDown := amount / divisor
player.Coins -= amount player.Coins -= amount
@ -408,7 +446,11 @@ func (g *WatGame) Rest(player *Player, fields []string) string {
} else if delta < minRest { } else if delta < minRest {
ret = fmt.Sprintf("wat were you thinking, sleeping at a time like this (%d until next rest)", minRest-delta) ret = fmt.Sprintf("wat were you thinking, sleeping at a time like this (%d until next rest)", minRest-delta)
} else { } else {
value := g.RandInt(10) + 1 value, randErr := g.RandInt(10)
if e := handleError(randErr); e != "" {
return e
}
value = value + 1
if player.Health < -5 { if player.Health < -5 {
player.Health = 1 player.Health = 1
ret = fmt.Sprintf("wow ur beat up. i pity u, ur health is now 1.") ret = fmt.Sprintf("wow ur beat up. i pity u, ur health is now 1.")
@ -435,8 +477,15 @@ func (g *WatGame) Bench(player *Player, fields []string) string {
if !g.CanAct(player, Action_Lift, minTime) { if !g.CanAct(player, Action_Lift, minTime) {
return "you're tired. no more lifting for now." return "you're tired. no more lifting for now."
} }
weight := g.RandInt(370) + 50 weight, randErr := g.RandInt(370)
reps := g.RandInt(10) if e := handleError(randErr); e != "" {
return e
}
weight = weight + 50
reps, randErr := g.RandInt(10)
if e := handleError(randErr); e != "" {
return e
}
value := int64(0) value := int64(0)
reply := fmt.Sprintf("%s benches %dwatts for %d reps, ", player.Nick, weight, reps) reply := fmt.Sprintf("%s benches %dwatts for %d reps, ", player.Nick, weight, reps)
if weight < 150 { if weight < 150 {
@ -452,7 +501,10 @@ func (g *WatGame) Bench(player *Player, fields []string) string {
} }
if g.roid[player.Nick] != 0 { if g.roid[player.Nick] != 0 {
delete(g.roid, player.Nick) delete(g.roid, player.Nick)
success := g.RandInt(2) success, randErr := g.RandInt(2)
if e := handleError(randErr); e != "" {
return e
}
if success != 0 { if success != 0 {
player.Health = 0 player.Health = 0
player.Anarchy -= 10 player.Anarchy -= 10
@ -475,7 +527,10 @@ func (g *WatGame) Riot(player *Player, fields []string) string {
if !g.CanAct(player, Action_Riot, int64((48 * time.Hour).Seconds())) { if !g.CanAct(player, Action_Riot, int64((48 * time.Hour).Seconds())) {
return "Planning a riot takes time and the right circumstances. Be prepared. (nothing happens)" return "Planning a riot takes time and the right circumstances. Be prepared. (nothing happens)"
} }
r := g.RandInt(100) r, randErr := g.RandInt(100)
if e := handleError(randErr); e != "" {
return e
}
reply := "" reply := ""
if r > 40 { if r > 40 {
player.Anarchy += 3 player.Anarchy += 3
@ -621,9 +676,21 @@ func PrintTwo(nick string, value uint64) string {
} }
func (g *WatGame) megaWat(player *Player, _ []string) string { func (g *WatGame) megaWat(player *Player, _ []string) string {
mega := g.RandInt(1000000) + 1 mega, randErr := g.RandInt(1000000)
kilo := g.RandInt(1000) + 1 if e := handleError(randErr); e != "" {
ten := g.RandInt(100) + 1 return e + " mega fail"
}
mega = mega + 1
kilo, randErr := g.RandInt(1000)
if e := handleError(randErr); e != "" {
return e + " kilo fail"
}
kilo = kilo + 1
ten, randErr := g.RandInt(100)
if e := handleError(randErr); e != "" {
return e + " ten fail"
}
ten = ten + 1
reply := "" reply := ""
if mega == 23 { if mega == 23 {
player.Coins += 1000000 player.Coins += 1000000

22
wat/utils.go Normal file
View File

@ -0,0 +1,22 @@
package wat
import (
"fmt"
"runtime"
)
func handleError(err error) string {
if err != nil {
pc, _, _, ok := runtime.Caller(1)
details := runtime.FuncForPC(pc)
var cFun string
if ok && details != nil {
cFun = details.Name()
} else {
cFun = "???"
}
fmt.Printf("caught error in %s: %v\n", cFun, err)
return "u wat"
}
return ""
}