Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a86d2b6ca | ||
|
|
c663b1a12c | ||
|
|
af42db6803 | ||
|
|
f9dc4cff9f |
1
go.mod
1
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/hajimehoshi/oto v0.7.1 // indirect
|
||||
github.com/micmonay/keybd_event v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -1,5 +1,7 @@
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
|
||||
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
@@ -15,6 +17,7 @@ github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk
|
||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
|
||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
|
||||
@@ -42,7 +42,7 @@ See `./config.d/rusty-pipe.d`
|
||||
|
||||
## STT
|
||||
|
||||
`cd /home/breel/Go/src/gogs.inhome.blapointe.com/stt.d/whisper-2023; HOTWORDS=/home/breel/Go/src/gogs.inhome.blapointe.com/mayhem-party.d/host.d/config.d/stt.d/hotwords.txt MIC_TIMEOUT=2 URL=http://localhost:17071/ HEADERS=say='what fool said, {{hotword}}, when they said, {{context}}?' python3 ./hotwords.py'
|
||||
`cd /home/breel/Go/src/gogs.inhome.blapointe.com/stt.d/whisper-2023; HOTWORDS=/home/breel/Go/src/gogs.inhome.blapointe.com/mayhem-party.d/host.d/config.d/stt.d/hotwords.txt MIC_TIMEOUT=2 URL=http://localhost:17071/config HEADERS=say='Eye herd {{hotword}}' BODY='[{"op":"replace", "path":"/Quiet", "value":true}]' python3 ./hotwords.py'
|
||||
|
||||
## `mayhem-party`
|
||||
|
||||
|
||||
@@ -1,16 +1,48 @@
|
||||
package v01
|
||||
|
||||
type config struct {
|
||||
Feedback struct {
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
patch "github.com/evanphx/json-patch/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
config struct {
|
||||
Feedback configFeedback
|
||||
Users map[string]configUser
|
||||
Players []configPlayer
|
||||
Quiet bool
|
||||
}
|
||||
|
||||
configFeedback struct {
|
||||
Addr string
|
||||
TTSURL string
|
||||
}
|
||||
Users map[string]struct {
|
||||
|
||||
configUser struct {
|
||||
Player int
|
||||
Message string
|
||||
}
|
||||
Players []struct {
|
||||
|
||||
configPlayer struct {
|
||||
Transformation transformation
|
||||
}
|
||||
Quiet bool
|
||||
)
|
||||
|
||||
func (cfg config) WithPatch(v interface{}) config {
|
||||
originalData, _ := json.Marshal(cfg)
|
||||
patchData, _ := json.Marshal(v)
|
||||
patcher, err := patch.DecodePatch(patchData)
|
||||
if err != nil {
|
||||
return cfg
|
||||
}
|
||||
patchedData, err := patcher.Apply(originalData)
|
||||
if err != nil {
|
||||
return cfg
|
||||
}
|
||||
var patched config
|
||||
if err := json.Unmarshal(patchedData, &patched); err != nil {
|
||||
return cfg
|
||||
}
|
||||
return patched
|
||||
}
|
||||
|
||||
79
src/device/input/parse/v01/config_test.go
Normal file
79
src/device/input/parse/v01/config_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package v01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigPatch(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
cfg config
|
||||
patch interface{}
|
||||
want config
|
||||
}{
|
||||
"nil patch": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: nil,
|
||||
want: config{Quiet: true},
|
||||
},
|
||||
"[] patch": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{},
|
||||
want: config{Quiet: true},
|
||||
},
|
||||
"set fake field": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "add", "path": "/Fake", "value": true},
|
||||
},
|
||||
want: config{Quiet: true},
|
||||
},
|
||||
"remove field": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "remove", "path": "/Quiet"},
|
||||
},
|
||||
want: config{Quiet: false},
|
||||
},
|
||||
"replace field with valid": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "replace", "path": "/Quiet", "value": false},
|
||||
},
|
||||
want: config{Quiet: false},
|
||||
},
|
||||
"replace field with invalid": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "replace", "path": "/Quiet", "value": "teehee"},
|
||||
},
|
||||
want: config{Quiet: true},
|
||||
},
|
||||
"test and noop": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "test", "path": "/Quiet", "value": false},
|
||||
map[string]interface{}{"op": "replace", "path": "/Quiet", "value": false},
|
||||
},
|
||||
want: config{Quiet: true},
|
||||
},
|
||||
"test and apply": {
|
||||
cfg: config{Quiet: true},
|
||||
patch: []interface{}{
|
||||
map[string]interface{}{"op": "test", "path": "/Quiet", "value": true},
|
||||
map[string]interface{}{"op": "replace", "path": "/Quiet", "value": false},
|
||||
},
|
||||
want: config{Quiet: false},
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
c := d
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := c.cfg.WithPatch(c.patch)
|
||||
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) {
|
||||
t.Errorf("(%+v).Patch(%+v) want %+v, got %+v", c.cfg, c.patch, c.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package v01
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"mayhem-party/src/device/input/wrap"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func (v01 *V01) listen() {
|
||||
@@ -55,6 +59,8 @@ func (v01 *V01) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
v01.getUserFeedback(w, r)
|
||||
case "/broadcast":
|
||||
v01.putBroadcast(w, r)
|
||||
case "/config":
|
||||
v01.patchConfig(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +79,21 @@ func (v01 *V01) putBroadcast(w http.ResponseWriter, r *http.Request) {
|
||||
v01.cfg.Users["broadcast"] = v
|
||||
}
|
||||
|
||||
func (v01 *V01) patchConfig(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := io.ReadAll(r.Body)
|
||||
var v []interface{}
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
v01.cfg = v01.cfg.WithPatch(v)
|
||||
if b, err := yaml.Marshal(v01.cfg); err == nil && FlagParseV01Config != "" {
|
||||
if err := os.WriteFile(FlagParseV01Config, b, os.ModePerm); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v01 *V01) globalQueries(r *http.Request) {
|
||||
v01.globalQuerySay(r)
|
||||
v01.globalQueryRefresh(r)
|
||||
|
||||
74
src/device/input/parse/v01/server_test.go
Normal file
74
src/device/input/parse/v01/server_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package v01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestPatchConfig(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
p := path.Join(dir, t.Name()+".yaml")
|
||||
cases := map[string]struct {
|
||||
was config
|
||||
patch string
|
||||
want config
|
||||
}{
|
||||
"replace entire doc": {
|
||||
was: config{
|
||||
Feedback: configFeedback{Addr: "a", TTSURL: "a"},
|
||||
Users: map[string]configUser{"a": configUser{Player: 1, Message: "a"}},
|
||||
Players: []configPlayer{configPlayer{Transformation: transformation{"a": "a"}}},
|
||||
Quiet: true,
|
||||
},
|
||||
patch: `[{"op": "replace", "path": "", "value": {
|
||||
"Feedback": {"Addr": "b", "TTSURL": "b"},
|
||||
"Users": {"b": {"Player": 2, "Message": "b"}},
|
||||
"Players": [{"Transformation": {"b": "b"}}],
|
||||
"Quiet": false
|
||||
}}]`,
|
||||
want: config{
|
||||
Feedback: configFeedback{Addr: "b", TTSURL: "b"},
|
||||
Users: map[string]configUser{"b": configUser{Player: 2, Message: "b"}},
|
||||
Players: []configPlayer{configPlayer{Transformation: transformation{"b": "b"}}},
|
||||
Quiet: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
c := d
|
||||
for _, usesdisk := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("%s disk=%v", name, usesdisk), func(t *testing.T) {
|
||||
b, _ := yaml.Marshal(c.was)
|
||||
os.WriteFile(p, b, os.ModePerm)
|
||||
FlagParseV01Config = ""
|
||||
if usesdisk {
|
||||
FlagParseV01Config = p
|
||||
}
|
||||
v01 := &V01{cfg: c.was}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodPatch, "/config", strings.NewReader(c.patch))
|
||||
v01.patchConfig(w, r)
|
||||
if fmt.Sprintf("%+v", c.want) != fmt.Sprintf("%+v", v01.cfg) {
|
||||
t.Errorf("want \n\t%+v, got \n\t%+v", c.want, v01.cfg)
|
||||
}
|
||||
if usesdisk {
|
||||
b, _ := os.ReadFile(p)
|
||||
var got config
|
||||
yaml.Unmarshal(b, &got)
|
||||
if fmt.Sprintf("%+v", c.want) != fmt.Sprintf("%+v", v01.cfg) {
|
||||
t.Errorf("want \n\t%+v, got \n\t%+v", c.want, v01.cfg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
todo:
|
||||
- https via home.blapointe and rproxy
|
||||
- json patch endpoint for config
|
||||
- endpoint for v01 to start read-only mode so when hotword spoken, players are dcd
|
||||
without losing players; press a hotkey that is bound to dolphin emulator pause
|
||||
- todo: rotation triggers
|
||||
|
||||
Reference in New Issue
Block a user