Merge pull request #92 from velour/sisyphus

Sisyphus
This commit is contained in:
Chris Sexton 2017-11-09 09:48:14 -05:00 committed by GitHub
commit dff629d367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 1 deletions

View File

@ -103,6 +103,12 @@ type Config struct {
Inventory struct { Inventory struct {
Max int Max int
} }
Sisyphus struct {
MinDecrement int
MaxDecrement int
MinPush int
MaxPush int
}
} }
func init() { func init() {

View File

@ -105,5 +105,14 @@ config = {
}, },
DBPath = "stats.db" DBPath = "stats.db"
}, },
HttpAddr = "127.0.0.1:1337" HttpAddr = "127.0.0.1:1337",
Inventory = {
Max = 5
},
Sisyphus = {
MinDecrement = 10,
MinPush = 1
}
}
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/velour/catbase/plugins/reminder" "github.com/velour/catbase/plugins/reminder"
"github.com/velour/catbase/plugins/rpgORdie" "github.com/velour/catbase/plugins/rpgORdie"
"github.com/velour/catbase/plugins/rss" "github.com/velour/catbase/plugins/rss"
"github.com/velour/catbase/plugins/sisyphus"
"github.com/velour/catbase/plugins/stats" "github.com/velour/catbase/plugins/stats"
"github.com/velour/catbase/plugins/talker" "github.com/velour/catbase/plugins/talker"
"github.com/velour/catbase/plugins/twitch" "github.com/velour/catbase/plugins/twitch"
@ -71,6 +72,7 @@ func main() {
b.AddHandler("twitch", twitch.New(b)) b.AddHandler("twitch", twitch.New(b))
b.AddHandler("inventory", inventory.New(b)) b.AddHandler("inventory", inventory.New(b))
b.AddHandler("rpgORdie", rpgORdie.New(b)) b.AddHandler("rpgORdie", rpgORdie.New(b))
b.AddHandler("sisyphus", sisyphus.New(b))
// catches anything left, will always return true // catches anything left, will always return true
b.AddHandler("factoid", fact.New(b)) b.AddHandler("factoid", fact.New(b))

View File

@ -0,0 +1,231 @@
package sisyphus
import (
"fmt"
"log"
"math/rand"
"strconv"
"strings"
"time"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
)
const (
BOULDER = ":full_moon:"
MOUNTAIN = ":new_moon:"
)
type SisyphusPlugin struct {
Bot bot.Bot
listenFor map[string]*game
}
type game struct {
id string
channel string
bot bot.Bot
who string
start time.Time
size int
current int
nextPush time.Time
nextDec time.Time
timers [2]*time.Timer
ended bool
nextAns int
}
func NewRandomGame(bot bot.Bot, channel, who string) *game {
size := rand.Intn(9) + 2
g := game{
channel: channel,
bot: bot,
who: who,
start: time.Now(),
size: size,
current: size / 2,
}
g.id = bot.SendMessage(channel, g.toMessageString())
g.schedulePush()
g.scheduleDecrement()
return &g
}
func (g *game) scheduleDecrement() {
if g.timers[0] != nil {
g.timers[0].Stop()
}
minDec := g.bot.Config().Sisyphus.MinDecrement
maxDec := g.bot.Config().Sisyphus.MinDecrement
g.nextDec = time.Now().Add(time.Duration((minDec + rand.Intn(maxDec))) * time.Minute)
go func() {
t := time.NewTimer(g.nextDec.Sub(time.Now()))
g.timers[0] = t
select {
case <-t.C:
g.handleDecrement()
}
}()
}
func (g *game) schedulePush() {
if g.timers[1] != nil {
g.timers[1].Stop()
}
minPush := g.bot.Config().Sisyphus.MinPush
maxPush := g.bot.Config().Sisyphus.MaxPush
g.nextPush = time.Now().Add(time.Duration(rand.Intn(maxPush)+minPush) * time.Minute)
go func() {
t := time.NewTimer(g.nextPush.Sub(time.Now()))
g.timers[1] = t
select {
case <-t.C:
g.handleNotify()
}
}()
}
func (g *game) endGame() {
for _, t := range g.timers {
t.Stop()
}
g.ended = true
}
func (g *game) handleDecrement() {
g.current++
g.bot.Edit(g.channel, g.toMessageString(), g.id)
if g.current > g.size-2 {
g.bot.ReplyToMessageIdentifier(g.channel, "you lose", g.id)
msg := fmt.Sprintf("%s just lost the game after %s", g.who, time.Now().Sub(g.start))
g.bot.SendMessage(g.channel, msg)
g.endGame()
} else {
g.scheduleDecrement()
}
}
func (g *game) handleNotify() {
g.bot.ReplyToMessageIdentifier(g.channel, "You can push now.\n"+g.generateQuestion(), g.id)
}
func (g *game) generateQuestion() string {
n1 := rand.Intn(99) + (rand.Intn(9)+1)*100
n2 := rand.Intn(99) + (rand.Intn(9)+1)*100
var op string
switch i := rand.Intn(3); i {
case 0:
// times
g.nextAns = n1 * n2
op = "*"
case 1:
// plus
g.nextAns = n1 + n2
op = "+"
case 2:
// minus
g.nextAns = n1 - n2
op = "-"
}
return fmt.Sprintf("What is %d %s %d?", n1, op, n2)
}
func (g *game) checkAnswer(ans string) bool {
if strings.Contains(ans, strconv.Itoa(g.nextAns)) {
g.current--
if g.current < 0 {
g.current = 0
}
return true
}
return false
}
func (g *game) toMessageString() string {
out := ""
for i := 0; i < g.size; i++ {
for j := 0; j < i; j++ {
out = out + MOUNTAIN
}
if i == g.current {
out = out + BOULDER
} else if i == g.current+1 {
out = out + ":" + g.who + ":"
}
out = out + "\n"
}
return out
}
func New(b bot.Bot) *SisyphusPlugin {
return &SisyphusPlugin{
Bot: b,
listenFor: map[string]*game{},
}
}
func (p *SisyphusPlugin) Message(message msg.Message) bool {
if strings.ToLower(message.Body) == "start sisyphus" {
b := NewRandomGame(p.Bot, message.Channel, message.User.Name)
p.listenFor[b.id] = b
p.Bot.ReplyToMessageIdentifier(message.Channel, "Over here.", b.id)
return true
}
return false
}
func (p *SisyphusPlugin) Help(channel string, parts []string) {
p.Bot.SendMessage(channel, "https://en.wikipedia.org/wiki/Sisyphus")
}
func (p *SisyphusPlugin) Event(kind string, message msg.Message) bool {
return false
}
func (p *SisyphusPlugin) BotMessage(message msg.Message) bool {
return false
}
func (p *SisyphusPlugin) RegisterWeb() *string {
return nil
}
func (p *SisyphusPlugin) ReplyMessage(message msg.Message, identifier string) bool {
if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Nick) {
if g, ok := p.listenFor[identifier]; ok {
log.Printf("got message on %s: %+v", identifier, message)
if g.ended {
return false
}
if strings.ToLower(message.Body) == "end game" {
g.endGame()
return true
}
if time.Now().After(g.nextPush) {
if g.checkAnswer(message.Body) {
p.Bot.Edit(message.Channel, g.toMessageString(), identifier)
g.schedulePush()
msg := fmt.Sprintf("Ok. You can push again in %s", g.nextPush.Sub(time.Now()))
p.Bot.ReplyToMessageIdentifier(message.Channel, msg, identifier)
} else {
p.Bot.ReplyToMessageIdentifier(message.Channel, "you lose", identifier)
msg := fmt.Sprintf("%s just lost the sisyphus game after %s", g.who, time.Now().Sub(g.start))
p.Bot.SendMessage(message.Channel, msg)
g.endGame()
}
} else {
p.Bot.ReplyToMessageIdentifier(message.Channel, "you cannot push yet", identifier)
}
return true
}
}
return false
}

View File

@ -0,0 +1 @@
package sisyphus

View File

@ -208,6 +208,7 @@ func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string
resp, err := http.PostForm(postUrl, resp, err := http.PostForm(postUrl,
url.Values{"token": {s.config.Slack.Token}, url.Values{"token": {s.config.Slack.Token},
"as_user": {"true"},
"channel": {channel}, "channel": {channel},
"text": {message}, "text": {message},
"as_user": {"true"}, "as_user": {"true"},
@ -258,6 +259,7 @@ func (s *Slack) SendAction(channel, message string) string {
func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) { func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
resp, err := http.PostForm("https://slack.com/api/chat.postMessage", resp, err := http.PostForm("https://slack.com/api/chat.postMessage",
url.Values{"token": {s.config.Slack.Token}, url.Values{"token": {s.config.Slack.Token},
"as_user": {"true"},
"channel": {channel}, "channel": {channel},
"text": {message}, "text": {message},
"as_user": {"true"}, "as_user": {"true"},