diff --git a/AUTHORS b/AUTHORS index 94139bc..49036db 100755 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Chris Sexton +Ethan Burns \ No newline at end of file diff --git a/main.go b/main.go index 9b9b5e3..6d8f86d 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/velour/catbase/config" "github.com/velour/catbase/irc" "github.com/velour/catbase/plugins/admin" + "github.com/velour/catbase/plugins/babbler" "github.com/velour/catbase/plugins/beers" "github.com/velour/catbase/plugins/counter" "github.com/velour/catbase/plugins/dice" @@ -17,9 +18,9 @@ import ( "github.com/velour/catbase/plugins/fact" "github.com/velour/catbase/plugins/leftpad" "github.com/velour/catbase/plugins/reminder" - "github.com/velour/catbase/plugins/babbler" "github.com/velour/catbase/plugins/talker" "github.com/velour/catbase/plugins/your" + "github.com/velour/catbase/plugins/zork" "github.com/velour/catbase/slack" ) @@ -55,6 +56,7 @@ func main() { b.AddHandler("counter", counter.New(b)) b.AddHandler("reminder", reminder.New(b)) b.AddHandler("babbler", babbler.New(b)) + b.AddHandler("zork", zork.New(b)) // catches anything left, will always return true b.AddHandler("factoid", fact.New(b)) diff --git a/plugins/zork/ZORK1.DAT b/plugins/zork/ZORK1.DAT new file mode 100644 index 0000000..5b79e24 Binary files /dev/null and b/plugins/zork/ZORK1.DAT differ diff --git a/plugins/zork/zork.go b/plugins/zork/zork.go new file mode 100644 index 0000000..7bd97bd --- /dev/null +++ b/plugins/zork/zork.go @@ -0,0 +1,124 @@ +// © 2016 the CatBase Authors under the WTFPL license. See AUTHORS for the list of authors. + +// Package zork implements a zork plugin for catbase. +package zork + +import ( + "bufio" + "bytes" + "go/build" + "io" + "log" + "os/exec" + "path/filepath" + "strings" + "sync" + + "github.com/velour/catbase/bot" + "github.com/velour/catbase/bot/msg" +) + +// ZorkPlugin is a catbase plugin for playing zork. +type ZorkPlugin struct { + bot bot.Bot + sync.Mutex + // zorks is a map from channels to their corresponding zork instances. + zorks map[string]io.WriteCloser +} + +func New(b bot.Bot) bot.Handler { + return &ZorkPlugin{ + bot: b, + zorks: make(map[string]io.WriteCloser), + } +} + +func (p *ZorkPlugin) runZork(ch string) error { + const importString = "github.com/velour/catbase/plugins/zork" + pkg, err := build.Import(importString, "", build.FindOnly) + if err != nil { + return err + } + zorkdat := filepath.Join(pkg.Dir, "ZORK1.DAT") + cmd := exec.Command("dfrotz", zorkdat) + + var r io.ReadCloser + r, cmd.Stdout = io.Pipe() + cmd.Stderr = cmd.Stdout + + var w io.WriteCloser + cmd.Stdin, w = io.Pipe() + + log.Printf("zork running %s\n", cmd) + if err := cmd.Start(); err != nil { + w.Close() + return err + } + go func() { + defer r.Close() + s := bufio.NewScanner(r) + // Scan until the next prompt. + s.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.Index(data, []byte{'\n', '>'}); i > 0 { + return i + 1, data[:i], nil + } + if atEOF { + return len(data), data, nil + } + return 0, nil, nil + }) + for s.Scan() { + // Remove > and quote the whole thing. + m := strings.Replace(s.Text(), ">", "", -1) + m = strings.Replace(m, "\n", "\n>", -1) + m = ">" + m + "\n" + p.bot.SendMessage(ch, m) + } + }() + go func() { + if err := cmd.Wait(); err != nil { + log.Printf("zork exited: %v\n", err) + } + p.Lock() + p.zorks[ch] = nil + p.Unlock() + }() + log.Printf("zork is running in %s\n", ch) + p.zorks[ch] = w + return nil +} + +func (p *ZorkPlugin) Message(message msg.Message) bool { + m := strings.ToLower(message.Body) + log.Printf("got message [%s]\n", m) + if ts := strings.Fields(m); len(ts) < 1 || ts[0] != "zork" { + return false + } + m = strings.TrimSpace(strings.TrimPrefix(m, "zork")) + ch := message.Channel + + p.Lock() + defer p.Unlock() + if p.zorks[ch] == nil { + if err := p.runZork(ch); err != nil { + p.bot.SendMessage(ch, "failed to run zork: "+err.Error()) + return true + } + } + log.Printf("zorking, [%s]\n", m) + io.WriteString(p.zorks[ch], m+"\n") + return true +} + +func (p *ZorkPlugin) Event(_ string, _ msg.Message) bool { return false } + +func (p *ZorkPlugin) BotMessage(_ msg.Message) bool { return false } + +func (p *ZorkPlugin) Help(ch string, _ []string) { + p.bot.SendMessage(ch, "Play zork using 'zork '.") +} + +func (p *ZorkPlugin) RegisterWeb() *string { return nil }