diff --git a/bot/bot.go b/bot/bot.go index e584ee1..6129e82 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -79,6 +79,7 @@ func New(config *config.Config, connector Connector) Bot { addr := config.Get("HttpAddr") if addr == "" { addr = "127.0.0.1:1337" + config.Set("HttpAddr", addr) } go http.ListenAndServe(addr, nil) @@ -172,7 +173,14 @@ func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) { // Checks if message is a command and returns its curtailed version func IsCmd(c *config.Config, message string) (bool, string) { cmdcs := c.GetArray("CommandChar") + if len(cmdcs) == 0 { + cmdcs = []string{"!"} + c.SetArray("CommandChar", cmdcs) + } botnick := strings.ToLower(c.Get("Nick")) + if botnick == "" { + log.Fatalf(`You must run catbase -set nick -val `) + } iscmd := false lowerMessage := strings.ToLower(message) diff --git a/config/config.go b/config/config.go index 2c540e3..6cb56f2 100644 --- a/config/config.go +++ b/config/config.go @@ -54,6 +54,12 @@ func (c *Config) Get(key string) string { return c.GetString(key) } +func envkey(key string) string { + key = strings.ToUpper(key) + key = strings.Replace(key, ".", "", -1) + return key +} + // GetString returns the config value for a string key // It will first look in the env vars for the key // It will check the DB for the key if an env DNE @@ -61,13 +67,14 @@ func (c *Config) Get(key string) string { // It will convert the value to a string if it exists func (c *Config) GetString(key string) string { key = strings.ToLower(key) - if v, found := os.LookupEnv(key); found { + if v, found := os.LookupEnv(envkey(key)); found { return v } var configValue string q := `select value from config where key=?` err := c.DB.Get(&configValue, q, key) if err != nil { + log.Printf("WARN: Key %s is empty", key) return "" } return configValue @@ -81,6 +88,9 @@ func (c *Config) GetString(key string) string { // This will do no conversion. func (c *Config) GetArray(key string) []string { val := c.GetString(key) + if val == "" { + return []string{} + } return strings.Split(val, ";;") } @@ -88,14 +98,19 @@ func (c *Config) GetArray(key string) []string { // Note, this is always a string. Use the SetArray for an array helper func (c *Config) Set(key, value string) error { key = strings.ToLower(key) - q := (`insert into config (key,value) values (?, ?);`) - _, err := c.Exec(q, key, value) + q := (`insert into config (key,value) values (?, ?) + on conflict(key) do update set value=?;`) + tx, err := c.Begin() if err != nil { - q := (`update config set value=? where key=?);`) - _, err = c.Exec(q, value, key) - if err != nil { - return err - } + return err + } + _, err = tx.Exec(q, key, value, value) + if err != nil { + return err + } + err = tx.Commit() + if err != nil { + return err } return nil } diff --git a/config/defaults.go b/config/defaults.go new file mode 100644 index 0000000..d98724c --- /dev/null +++ b/config/defaults.go @@ -0,0 +1,57 @@ +package config + +import ( + "bytes" + "html/template" + "log" + "strings" +) + +var q = ` +INSERT INTO config VALUES('type','slack'); +INSERT INTO config VALUES('nick','{{.Nick}}'); +INSERT INTO config VALUES('channels','{{.Channel}}'); +INSERT INTO config VALUES('factoid.quotetime',30); +INSERT INTO config VALUES('reaction.negativereactions','bullshit;;fake;;tableflip;;vomit'); +INSERT INTO config VALUES('reaction.positivereactions','+1;;authorized;;aw_yeah;;yeah_man;;joy'); +INSERT INTO config VALUES('reaction.generalchance',0.01); +INSERT INTO config VALUES('reaction.harrasschance',0.05); +INSERT INTO config VALUES('commandchar','!;;ยก'); +INSERT INTO config VALUES('factoid.startupfact','speed test'); +INSERT INTO config VALUES('factoid.quotechance',0.99); +INSERT INTO config VALUES('factoid.minlen',4); +INSERT INTO config VALUES('untappd.channels','{{.Channel}}'); +INSERT INTO config VALUES('twitch.channels','{{.Channel}}'); +INSERT INTO config VALUES('twitch.{{.ChannelKey}}.users','drseabass;;phlyingpenguin;;stack5;;geoffwithaj;;msherms;;eaburns;;sheltim;;rathaus;;rcuhljr'); +INSERT INTO config VALUES('twitch.freq',60); +INSERT INTO config VALUES('leftpad.maxlen',50); +INSERT INTO config VALUES('untappd.freq',60); +INSERT INTO config VALUES('your.replacements.0.freq',1); +INSERT INTO config VALUES('your.replacements.0.this','fuck'); +INSERT INTO config VALUES('your.replacements.0.that','duck'); +INSERT INTO config VALUES('your.replacements','0;;1;;2'); +INSERT INTO config VALUES('httpaddr','127.0.0.1:1337'); +INSERT INTO config VALUES('your.maxlength',140); +INSERT INTO config VALUES('init',1); +` + +func (c *Config) SetDefaults(mainChannel, nick string) { + if nick == mainChannel && nick == "" { + log.Fatalf("You must provide a nick and a mainChannel") + } + t := template.Must(template.New("query").Parse(q)) + vals := struct { + Nick string + Channel string + ChannelKey string + }{ + nick, + mainChannel, + strings.ToLower(mainChannel), + } + var buf bytes.Buffer + t.Execute(&buf, vals) + c.MustExec(`delete from config;`) + c.MustExec(buf.String()) + log.Println("Configuration initialized.") +} diff --git a/main.go b/main.go index 094e648..3b77307 100644 --- a/main.go +++ b/main.go @@ -38,6 +38,12 @@ import ( "github.com/velour/catbase/slack" ) +var ( + key = flag.String("set", "", "Configuration key to set") + val = flag.String("val", "", "Configuration value to set") + initDB = flag.Bool("init", false, "Initialize the configuration DB") +) + func main() { rand.Seed(time.Now().Unix()) @@ -46,8 +52,26 @@ func main() { flag.Parse() // parses the logging flags. c := config.ReadConfig(*dbpath) + + if *key != "" && *val != "" { + c.Set(*key, *val) + log.Printf("Set config %s: %s", *key, *val) + return + } + if (*initDB && len(flag.Args()) != 2) || (!*initDB && c.GetInt("init") != 1) { + log.Fatal(`You must run "catbase -init "`) + } else if *initDB { + c.SetDefaults(flag.Arg(0), flag.Arg(1)) + return + } + var client bot.Connector + t := c.Get("type") + if t == "" { + c.Set("type", "slack") + t = "slack" + } switch c.Get("type") { case "irc": client = irc.New(c) diff --git a/plugins/beers/beers.go b/plugins/beers/beers.go index 3e91869..4d4477c 100644 --- a/plugins/beers/beers.go +++ b/plugins/beers/beers.go @@ -306,7 +306,12 @@ type Beers struct { } func (p *BeersPlugin) pullUntappd() ([]checkin, error) { - access_token := "?access_token=" + p.Bot.Config().Get("Untappd.Token") + token := p.Bot.Config().Get("Untappd.Token") + if token == "" { + return []checkin{}, fmt.Errorf("No untappd token") + } + + access_token := "?access_token=" + token baseUrl := "https://api.untappd.com/v4/checkin/recent/" url := baseUrl + access_token + "&limit=25" @@ -337,7 +342,8 @@ func (p *BeersPlugin) pullUntappd() ([]checkin, error) { func (p *BeersPlugin) checkUntappd(channel string) { token := p.Bot.Config().Get("Untappd.Token") - if token == "" || token == "" { + if token == "" { + log.Println(`Set config value "untappd.token" if you wish to enable untappd`) return } @@ -421,6 +427,9 @@ func (p *BeersPlugin) checkUntappd(channel string) { func (p *BeersPlugin) untappdLoop(channel string) { frequency := p.Bot.Config().GetInt("Untappd.Freq") + if frequency == 0 { + return + } log.Println("Checking every ", frequency, " seconds") diff --git a/plugins/fact/factoid.go b/plugins/fact/factoid.go index f216ccc..bf989b8 100644 --- a/plugins/fact/factoid.go +++ b/plugins/fact/factoid.go @@ -691,7 +691,12 @@ func (p *Factoid) randomFact() *factoid { // factTimer spits out a fact at a given interval and with given probability func (p *Factoid) factTimer(channel string) { - duration := time.Duration(p.Bot.Config().GetInt("Factoid.QuoteTime")) * time.Minute + quoteTime := p.Bot.Config().GetInt("Factoid.QuoteTime") + if quoteTime == 0 { + quoteTime = 30 + p.Bot.Config().Set("Factoid.QuoteTime", "30") + } + duration := time.Duration(quoteTime) * time.Minute myLastMsg := time.Now() for { time.Sleep(time.Duration(5) * time.Second) // why 5? @@ -705,7 +710,12 @@ func (p *Factoid) factTimer(channel string) { tdelta := time.Since(lastmsg.Time) earlier := time.Since(myLastMsg) > tdelta chance := rand.Float64() - success := chance < p.Bot.Config().GetFloat64("Factoid.QuoteChance") + quoteChance := p.Bot.Config().GetFloat64("Factoid.QuoteChance") + if quoteChance == 0.0 { + quoteChance = 0.99 + p.Bot.Config().Set("Factoid.QuoteChance", "0.99") + } + success := chance < quoteChance if success && tdelta > duration && earlier { fact := p.randomFact() diff --git a/plugins/inventory/inventory.go b/plugins/inventory/inventory.go index 83e767c..5166f04 100644 --- a/plugins/inventory/inventory.go +++ b/plugins/inventory/inventory.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "regexp" + "strconv" "strings" "github.com/jmoiron/sqlx" @@ -201,7 +202,12 @@ func (p *InventoryPlugin) addItem(m msg.Message, i string) bool { return true } var removed string - if p.count() > p.config.GetInt("Inventory.Max") { + max := p.config.GetInt("inventory.max") + if max == 0 { + max = 10 + p.config.Set("inventory.max", strconv.Itoa(max)) + } + if p.count() > max { removed = p.removeRandom() } _, err := p.Exec(`INSERT INTO inventory (item) values (?)`, i) diff --git a/plugins/leftpad/leftpad.go b/plugins/leftpad/leftpad.go index 67189bf..d068ae1 100644 --- a/plugins/leftpad/leftpad.go +++ b/plugins/leftpad/leftpad.go @@ -45,7 +45,12 @@ func (p *LeftpadPlugin) Message(message msg.Message) bool { p.bot.SendMessage(message.Channel, "Invalid padding number") return true } - if length > p.config.GetInt("LeftPad.MaxLen") && p.config.GetInt("LeftPad.MaxLen") > 0 { + maxLen, who := p.config.GetInt("LeftPad.MaxLen"), p.config.Get("LeftPad.Who") + if who == "" { + who = "Putin" + p.config.Set("LeftPad.MaxLen", who) + } + if length > maxLen && maxLen > 0 { msg := fmt.Sprintf("%s would kill me if I did that.", p.config.Get("LeftPad.Who")) p.bot.SendMessage(message.Channel, msg) return true diff --git a/plugins/leftpad/leftpad_test.go b/plugins/leftpad/leftpad_test.go index 4f5511f..cb57c25 100644 --- a/plugins/leftpad/leftpad_test.go +++ b/plugins/leftpad/leftpad_test.go @@ -31,6 +31,7 @@ func makePlugin(t *testing.T) (*LeftpadPlugin, *bot.MockBot) { counter.New(mb) p := New(mb) assert.NotNil(t, p) + p.config.Set("LeftPad.MaxLen", "0") return p, mb } @@ -56,6 +57,7 @@ func TestNotCommand(t *testing.T) { func TestNoMaxLen(t *testing.T) { p, mb := makePlugin(t) + p.config.Set("LeftPad.MaxLen", "0") p.Message(makeMessage("!leftpad dicks 100 dicks")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "dicks") diff --git a/plugins/reminder/reminder.go b/plugins/reminder/reminder.go index b618289..49ca760 100644 --- a/plugins/reminder/reminder.go +++ b/plugins/reminder/reminder.go @@ -122,7 +122,12 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { what := strings.Join(parts[6:], " ") for i := 0; when.Before(endTime); i++ { - if i >= p.config.GetInt("Reminder.MaxBatchAdd") { + max := p.config.GetInt("Reminder.MaxBatchAdd") + if max == 0 { + max = 10 + p.config.Set("reminder.maxbatchadd", strconv.Itoa(max)) + } + if i >= max { p.Bot.SendMessage(channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.") doConfirm = false break diff --git a/plugins/sisyphus/sisyphus.go b/plugins/sisyphus/sisyphus.go index 834e1b8..c73cbd9 100644 --- a/plugins/sisyphus/sisyphus.go +++ b/plugins/sisyphus/sisyphus.go @@ -60,7 +60,13 @@ func (g *game) scheduleDecrement() { g.timers[0].Stop() } minDec := g.bot.Config().GetInt("Sisyphus.MinDecrement") - maxDec := g.bot.Config().GetInt("Sisyphus.MinDecrement") + maxDec := g.bot.Config().GetInt("Sisyphus.MaxDecrement") + if maxDec == minDec && maxDec == 0 { + maxDec = 30 + minDec = 10 + g.bot.Config().Set("Sisyphus.MinDecrement", strconv.Itoa(minDec)) + g.bot.Config().Set("Sisyphus.MaxDecrement", strconv.Itoa(maxDec)) + } g.nextDec = time.Now().Add(time.Duration((minDec + rand.Intn(maxDec))) * time.Minute) go func() { t := time.NewTimer(g.nextDec.Sub(time.Now())) @@ -78,6 +84,12 @@ func (g *game) schedulePush() { } minPush := g.bot.Config().GetInt("Sisyphus.MinPush") maxPush := g.bot.Config().GetInt("Sisyphus.MaxPush") + if minPush == maxPush && maxPush == 0 { + minPush = 1 + maxPush = 10 + g.bot.Config().Set("Sisyphus.MinPush", strconv.Itoa(minPush)) + g.bot.Config().Set("Sisyphus.MaxPush", strconv.Itoa(maxPush)) + } g.nextPush = time.Now().Add(time.Duration(rand.Intn(maxPush)+minPush) * time.Minute) go func() { t := time.NewTimer(g.nextPush.Sub(time.Now())) diff --git a/plugins/your/your.go b/plugins/your/your.go index 59d1222..9f6ad47 100644 --- a/plugins/your/your.go +++ b/plugins/your/your.go @@ -4,6 +4,7 @@ package your import ( "math/rand" + "strconv" "strings" "github.com/velour/catbase/bot" @@ -28,7 +29,12 @@ func New(bot bot.Bot) *YourPlugin { // This function returns true if the plugin responds in a meaningful way to the users message. // Otherwise, the function returns false and the bot continues execution of other plugins. func (p *YourPlugin) Message(message msg.Message) bool { - if len(message.Body) > p.config.GetInt("Your.MaxLength") { + maxLen := p.config.GetInt("your.maxlength") + if maxLen == 0 { + maxLen = 140 + p.config.Set("your.maxlength", strconv.Itoa(maxLen)) + } + if len(message.Body) > maxLen { return false } msg := message.Body diff --git a/slack/slack.go b/slack/slack.go index f6ba143..c7b3981 100644 --- a/slack/slack.go +++ b/slack/slack.go @@ -212,6 +212,10 @@ func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string nick := s.config.Get("Nick") icon := s.config.Get("IconURL") + if icon == "" { + icon = "https://placekitten.com/400/400" + log.Println("Set config item IconURL to customize appearance!") + } resp, err := http.PostForm(postUrl, url.Values{"token": {s.config.Get("Slack.Token")},