catbase/plugins/zork/zork.go

124 lines
2.9 KiB
Go
Raw Normal View History

2016-05-15 18:13:34 +00:00
// © 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"
"os/exec"
"path/filepath"
"strings"
"sync"
2019-03-07 16:35:42 +00:00
"github.com/rs/zerolog/log"
2016-05-15 18:13:34 +00:00
"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.Plugin {
z := &ZorkPlugin{
2016-05-15 18:13:34 +00:00
bot: b,
zorks: make(map[string]io.WriteCloser),
}
b.Register(z, bot.Message, z.message)
b.Register(z, bot.Help, z.help)
return z
2016-05-15 18:13:34 +00:00
}
2019-05-27 23:21:53 +00:00
func (p *ZorkPlugin) runZork(c bot.Connector, ch string) error {
2016-05-15 18:13:34 +00:00
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()
2019-03-07 16:35:42 +00:00
log.Info().Msgf("zork running %v", cmd)
2016-05-15 18:13:34 +00:00
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"
2019-05-27 23:21:53 +00:00
p.bot.Send(c, bot.Message, ch, m)
2016-05-15 18:13:34 +00:00
}
}()
go func() {
if err := cmd.Wait(); err != nil {
2019-03-07 16:35:42 +00:00
log.Error().Err(err).Msg("zork exited")
2016-05-15 18:13:34 +00:00
}
p.Lock()
p.zorks[ch] = nil
p.Unlock()
}()
2019-03-07 16:35:42 +00:00
log.Info().Msgf("zork is running in %s\n", ch)
2016-05-15 18:13:34 +00:00
p.zorks[ch] = w
return nil
}
2019-05-27 23:21:53 +00:00
func (p *ZorkPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
2016-05-15 18:13:34 +00:00
m := strings.ToLower(message.Body)
2019-03-07 16:35:42 +00:00
log.Debug().Msgf("got message [%s]", m)
2016-05-15 18:13:34 +00:00
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 {
2019-05-27 23:21:53 +00:00
if err := p.runZork(c, ch); err != nil {
p.bot.Send(c, bot.Message, ch, "failed to run zork: "+err.Error())
2016-05-15 18:13:34 +00:00
return true
}
}
2019-03-07 16:35:42 +00:00
log.Debug().Msgf("zorking, [%s]", m)
2016-05-15 18:13:34 +00:00
io.WriteString(p.zorks[ch], m+"\n")
return true
}
2019-05-27 23:21:53 +00:00
func (p *ZorkPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(c, bot.Message, message.Channel, "Play zork using 'zork <zork command>'.")
return true
2016-05-15 18:13:34 +00:00
}