package button import ( "context" "encoding/json" "io" "io/ioutil" "log" "mayhem-party/src/device/input/raw" "net/http" "os" "time" "gopkg.in/yaml.v2" ) var ( FlagDebug = os.Getenv("DEBUG") == "true" FlagButtonV01Config = os.Getenv("BUTTON_V01_CONFIG") ) type ( V01 struct { ctx context.Context can context.CancelFunc src raw.Raw cfg v01Cfg } v01Msg struct { T int64 U string Y string N string } v01Cfg struct { Feedback struct { Addr string } Users map[string]struct { Player int Message string } Players []struct { Transformation v01Transformation } } v01Transformation map[string]string ) func NewV01(ctx context.Context, src raw.Raw) *V01 { var cfg v01Cfg b, _ := ioutil.ReadFile(FlagButtonV01Config) yaml.Unmarshal(b, &cfg) ctx, can := context.WithCancel(ctx) result := &V01{ ctx: ctx, can: can, src: src, cfg: cfg, } go result.listen() return result } func (v01 *V01) listen() { if v01.cfg.Feedback.Addr == "" { return } s := &http.Server{ Addr: v01.cfg.Feedback.Addr, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { r = r.WithContext(v01.ctx) user, ok := v01.cfg.Users[r.URL.Query().Get("user")] if !ok { user = v01.cfg.Users["broadcast"] } w.Write([]byte(user.Message)) } else if r.URL.Path == "/broadcast" { b, _ := io.ReadAll(r.Body) v := v01.cfg.Users["broadcast"] v.Message = string(b) v01.cfg.Users["broadcast"] = v } }), } go func() { <-v01.ctx.Done() log.Println("closing v01 server") s.Close() }() log.Println("starting v01 server") if err := s.ListenAndServe(); err != nil && v01.ctx.Err() == nil { log.Println("err with v01 server", err) panic(err) } } func (v01 *V01) CloseWrap() raw.Raw { v01.can() return v01.src } func (v01 *V01) Close() { v01.can() v01.src.Close() } func (v01 *V01) Read() []Button { line := v01.src.Read() var msg v01Msg if err := json.Unmarshal(line, &msg); err != nil { log.Printf("%v: %s", err, line) } v01.telemetry(msg) return v01.cfg.transform(msg).buttons() } func (cfg v01Cfg) transform(msg v01Msg) v01Msg { if len(cfg.Players) == 0 { return msg } user := cfg.Users[msg.U] if user.Player < 1 { msg.Y = "" msg.N = "" return msg } player := cfg.Players[user.Player-1] msg.Y = player.Transformation.pipe(msg.Y) msg.N = player.Transformation.pipe(msg.N) return msg } func (t v01Transformation) pipe(s string) string { for i := range s { if v := t[s[i:i+1]]; v != "" { s = s[:i] + v[:1] + s[i+1:] } } return s } func (v01 *V01) telemetry(msg v01Msg) { if FlagDebug { log.Printf("%s|%dms", msg.U, time.Now().UnixNano()/int64(time.Millisecond)-msg.T) } } func (msg v01Msg) buttons() []Button { buttons := make([]Button, len(msg.Y)+len(msg.N)) for i := range msg.Y { buttons[i] = Button{Char: msg.Y[i], Down: true} } for i := range msg.N { buttons[len(msg.Y)+i] = Button{Char: msg.N[i], Down: false} } if FlagDebug { log.Printf("%+v", msg) } return buttons }