Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67e504ced6 | ||
|
|
ad967d5047 | ||
|
|
8fd0067ad1 | ||
|
|
43566be7ae | ||
|
|
cb8b254cbb | ||
|
|
340ca1d2f5 | ||
|
|
02c49852c0 | ||
|
|
02c9dce1b3 | ||
|
|
a3650642ca | ||
|
|
fbded57807 | ||
|
|
44cb05487e | ||
|
|
e1e2ce3eec | ||
|
|
4c7f444887 | ||
|
|
0311fc56a3 | ||
|
|
9902684990 | ||
|
|
967e66bdb3 | ||
|
|
ff21bfb8b3 | ||
|
|
c153636e24 | ||
|
|
efe4adf129 | ||
|
|
802266e500 | ||
|
|
373d8be1a0 | ||
|
|
bd5654128e | ||
|
|
9073658e12 | ||
|
|
7df4d09553 | ||
|
|
1ad60189f4 | ||
|
|
766c77b00a | ||
|
|
bcdf545188 |
16
go.mod
16
go.mod
@@ -3,7 +3,17 @@ module mayhem-party
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
|
github.com/faiface/beep v1.1.0
|
||||||
github.com/micmonay/keybd_event v1.1.1 // indirect
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
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
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 // indirect
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 // indirect
|
||||||
|
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
37
go.sum
37
go.sum
@@ -1,7 +1,44 @@
|
|||||||
|
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/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=
|
||||||
|
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||||
|
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
||||||
|
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
||||||
|
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
|
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||||
|
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||||
|
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/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=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
||||||
|
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
||||||
github.com/micmonay/keybd_event v1.1.1 h1:rv7omwXWYL9Lgf3PUq6uBgJI2k1yGkL/GD6dxc6nmSs=
|
github.com/micmonay/keybd_event v1.1.1 h1:rv7omwXWYL9Lgf3PUq6uBgJI2k1yGkL/GD6dxc6nmSs=
|
||||||
github.com/micmonay/keybd_event v1.1.1/go.mod h1:CGMWMDNgsfPljzrAWoybUOSKafQPZpv+rLigt2LzNGI=
|
github.com/micmonay/keybd_event v1.1.1/go.mod h1:CGMWMDNgsfPljzrAWoybUOSKafQPZpv+rLigt2LzNGI=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|||||||
@@ -11,9 +11,16 @@
|
|||||||
## Distribute `rusty-pipe`
|
## Distribute `rusty-pipe`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# https://www.reddit.com/r/rust/comments/5k8uab/crosscompiling_from_ubuntu_to_windows_with_rustup/
|
||||||
|
(
|
||||||
|
echo '[target.x86_64-pc-windows-gnu]'
|
||||||
|
echo 'linker = "x86_64-w64-mingw32-gcc"'
|
||||||
|
echo 'ar = "x86_64-w64-mingw32-gcc-ar"'
|
||||||
|
) >> $HOME/.cargo/config
|
||||||
|
sudo apt install mingw-w64
|
||||||
rustup target add x86_64-pc-windows-gnu
|
rustup target add x86_64-pc-windows-gnu
|
||||||
echo windows
|
echo windows
|
||||||
cargo build --target x86_64-pc-windows-gnu && ls target/x86_64-pc-windows-gnu/release/rusty-pipe.exe
|
cargo build --release --target x86_64-pc-windows-gnu && ls target/x86_64-pc-windows-gnu/release/rusty-pipe.exe
|
||||||
echo local
|
echo local
|
||||||
cargo install --path ./
|
cargo install --path ./
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ streams:
|
|||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: bel
|
user: bel
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=bel
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ streams:
|
|||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: zach
|
user: zach
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=zach
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ streams:
|
|||||||
engine:
|
engine:
|
||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: mason
|
user: chase
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=chase
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ streams:
|
|||||||
engine:
|
engine:
|
||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: nat
|
user: mason
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=mason
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ streams:
|
|||||||
engine:
|
engine:
|
||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: roxy
|
user: nat
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=nat
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ streams:
|
|||||||
engine:
|
engine:
|
||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: chase
|
user: roxy
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=roxy
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ streams:
|
|||||||
name: gui
|
name: gui
|
||||||
gui:
|
gui:
|
||||||
user: bill
|
user: bill
|
||||||
|
feedback:
|
||||||
|
url: http://mayhem-party.home.blapointe.com:17071?user=bill
|
||||||
press: {prefix: "", suffix: ""}
|
press: {prefix: "", suffix: ""}
|
||||||
release: {prefix: "", suffix: ""}
|
release: {prefix: "", suffix: ""}
|
||||||
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
format: '{"T":{{ms}},"U":"{{user}}","Y":"{{pressed}}","N":"{{released}}"}'
|
||||||
@@ -20,7 +22,7 @@ streams:
|
|||||||
x: '3'
|
x: '3'
|
||||||
y: '4'
|
y: '4'
|
||||||
output:
|
output:
|
||||||
debug: true
|
debug: false
|
||||||
engine:
|
engine:
|
||||||
name: udp
|
name: udp
|
||||||
udp:
|
udp:
|
||||||
|
|||||||
1
host.d/config.d/rusty-pipe.d/rusty-pipe.exe
Symbolic link
1
host.d/config.d/rusty-pipe.d/rusty-pipe.exe
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../rusty-pipe.d/target/x86_64-pc-windows-gnu/release/rusty-pipe.exe
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package button
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"mayhem-party/src/device/input/raw"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
FlagButtonV01 = os.Getenv("BUTTON_V01") == "true"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Parser interface {
|
|
||||||
Read() []Button
|
|
||||||
Close()
|
|
||||||
CloseWrap() raw.Raw
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(ctx context.Context, src raw.Raw) Parser {
|
|
||||||
if FlagButtonV01 {
|
|
||||||
return NewV01(ctx, src)
|
|
||||||
}
|
|
||||||
return NewPlaintext(src)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package button_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayhem-party/src/device/input/button"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParser(t *testing.T) {
|
|
||||||
var _ button.Parser = button.Plaintext{}
|
|
||||||
var _ button.Parser = button.V01{}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package button
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"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) {
|
|
||||||
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))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package input
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/parse"
|
||||||
"mayhem-party/src/device/input/raw"
|
"mayhem-party/src/device/input/raw"
|
||||||
"mayhem-party/src/device/input/wrap"
|
"mayhem-party/src/device/input/wrap"
|
||||||
)
|
)
|
||||||
@@ -14,7 +15,7 @@ type Input interface {
|
|||||||
|
|
||||||
func New(ctx context.Context) Input {
|
func New(ctx context.Context) Input {
|
||||||
src := raw.New(ctx)
|
src := raw.New(ctx)
|
||||||
return wrap.New(ctx, func() button.Parser {
|
return wrap.New(ctx, func() wrap.Wrap {
|
||||||
return button.New(ctx, src)
|
return parse.New(ctx, src)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/device/input/parse/parser.go
Normal file
26
src/device/input/parse/parser.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package parse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
|
v01 "mayhem-party/src/device/input/parse/v01"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagParseV01 = os.Getenv("PARSE_V01") == "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser interface {
|
||||||
|
Read() []button.Button
|
||||||
|
Close()
|
||||||
|
CloseWrap() raw.Raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, src raw.Raw) Parser {
|
||||||
|
if FlagParseV01 {
|
||||||
|
return v01.NewV01(ctx, src)
|
||||||
|
}
|
||||||
|
return NewPlaintext(src)
|
||||||
|
}
|
||||||
12
src/device/input/parse/parser_test.go
Normal file
12
src/device/input/parse/parser_test.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package parse_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayhem-party/src/device/input/parse"
|
||||||
|
v01 "mayhem-party/src/device/input/parse/v01"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParser(t *testing.T) {
|
||||||
|
var _ parse.Parser = parse.Plaintext{}
|
||||||
|
var _ parse.Parser = &v01.V01{}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package button
|
package parse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
"mayhem-party/src/device/input/raw"
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
FlagButtonPlaintextRelease = os.Getenv("BUTTON_PLAINTEXT_RELEASE")
|
FlagParsePlaintextRelease = os.Getenv("PARSE_PLAINTEXT_RELEASE")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Plaintext struct {
|
type Plaintext struct {
|
||||||
@@ -16,8 +17,8 @@ type Plaintext struct {
|
|||||||
|
|
||||||
func NewPlaintext(src raw.Raw) Plaintext {
|
func NewPlaintext(src raw.Raw) Plaintext {
|
||||||
releaseChar := byte('!')
|
releaseChar := byte('!')
|
||||||
if FlagButtonPlaintextRelease != "" {
|
if FlagParsePlaintextRelease != "" {
|
||||||
releaseChar = byte(FlagButtonPlaintextRelease[0])
|
releaseChar = byte(FlagParsePlaintextRelease[0])
|
||||||
}
|
}
|
||||||
return Plaintext{
|
return Plaintext{
|
||||||
src: src,
|
src: src,
|
||||||
@@ -29,16 +30,16 @@ func (p Plaintext) Close() { p.src.Close() }
|
|||||||
|
|
||||||
func (p Plaintext) CloseWrap() raw.Raw { return p.src }
|
func (p Plaintext) CloseWrap() raw.Raw { return p.src }
|
||||||
|
|
||||||
func (p Plaintext) Read() []Button {
|
func (p Plaintext) Read() []button.Button {
|
||||||
b := p.src.Read()
|
b := p.src.Read()
|
||||||
buttons := make([]Button, 0, len(b))
|
buttons := make([]button.Button, 0, len(b))
|
||||||
down := true
|
down := true
|
||||||
for i := range b {
|
for i := range b {
|
||||||
if b[i] == p.release {
|
if b[i] == p.release {
|
||||||
down = false
|
down = false
|
||||||
} else {
|
} else {
|
||||||
if b[i] != '\n' {
|
if b[i] != '\n' {
|
||||||
buttons = append(buttons, Button{Char: b[i], Down: down})
|
buttons = append(buttons, button.Button{Char: b[i], Down: down})
|
||||||
}
|
}
|
||||||
down = true
|
down = true
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package button_test
|
package parse_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/parse"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPlaintext(t *testing.T) {
|
func TestPlaintext(t *testing.T) {
|
||||||
src := constSrc("c!b")
|
src := constSrc("c!b")
|
||||||
p := button.NewPlaintext(src)
|
p := parse.NewPlaintext(src)
|
||||||
got := p.Read()
|
got := p.Read()
|
||||||
if len(got) != 2 {
|
if len(got) != 2 {
|
||||||
t.Fatal(len(got))
|
t.Fatal(len(got))
|
||||||
15
src/device/input/parse/v01/config.go
Normal file
15
src/device/input/parse/v01/config.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Feedback struct {
|
||||||
|
Addr string
|
||||||
|
TTSURL string
|
||||||
|
}
|
||||||
|
Users map[string]struct {
|
||||||
|
Player int
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
Players []struct {
|
||||||
|
Transformation transformation
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/device/input/parse/v01/message.go
Normal file
27
src/device/input/parse/v01/message.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
T int64
|
||||||
|
U string
|
||||||
|
Y string
|
||||||
|
N string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg message) buttons() []button.Button {
|
||||||
|
buttons := make([]button.Button, len(msg.Y)+len(msg.N))
|
||||||
|
for i := range msg.Y {
|
||||||
|
buttons[i] = button.Button{Char: msg.Y[i], Down: true}
|
||||||
|
}
|
||||||
|
for i := range msg.N {
|
||||||
|
buttons[len(msg.Y)+i] = button.Button{Char: msg.N[i], Down: false}
|
||||||
|
}
|
||||||
|
if FlagDebug {
|
||||||
|
log.Printf("%+v", msg)
|
||||||
|
}
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
100
src/device/input/parse/v01/server.go
Normal file
100
src/device/input/parse/v01/server.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"mayhem-party/src/device/input/wrap"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v01 *V01) listen() {
|
||||||
|
if v01.cfg.Feedback.Addr == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v01._listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) _listen() {
|
||||||
|
mutex := &sync.RWMutex{}
|
||||||
|
s := &http.Server{
|
||||||
|
Addr: v01.cfg.Feedback.Addr,
|
||||||
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodGet {
|
||||||
|
mutex.RLock()
|
||||||
|
defer mutex.RUnlock()
|
||||||
|
} else {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
}
|
||||||
|
v01.ServeHTTP(w, r)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
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) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r = r.WithContext(v01.ctx)
|
||||||
|
v01.serveHTTP(w, r)
|
||||||
|
v01.globalQueries(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/":
|
||||||
|
v01.getUserFeedback(w, r)
|
||||||
|
case "/broadcast":
|
||||||
|
v01.putBroadcast(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) getUserFeedback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, ok := v01.cfg.Users[r.URL.Query().Get("user")]
|
||||||
|
if !ok {
|
||||||
|
user = v01.cfg.Users["broadcast"]
|
||||||
|
}
|
||||||
|
w.Write([]byte(user.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) putBroadcast(w http.ResponseWriter, r *http.Request) {
|
||||||
|
b, _ := io.ReadAll(r.Body)
|
||||||
|
v := v01.cfg.Users["broadcast"]
|
||||||
|
v.Message = string(b)
|
||||||
|
v01.cfg.Users["broadcast"] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) globalQueries(r *http.Request) {
|
||||||
|
v01.globalQuerySay(r)
|
||||||
|
v01.globalQueryRefresh(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) globalQuerySay(r *http.Request) {
|
||||||
|
text := r.URL.Query().Get("say")
|
||||||
|
if text == "" {
|
||||||
|
text = r.Header.Get("say")
|
||||||
|
}
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go v01.tts(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) globalQueryRefresh(r *http.Request) {
|
||||||
|
if _, ok := r.URL.Query()["refresh"]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case wrap.ChSigUsr1 <- syscall.SIGUSR1:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
feedback:
|
||||||
|
addr: :17071
|
||||||
|
ttsurl: http://localhost:15002
|
||||||
users:
|
users:
|
||||||
bel:
|
bel:
|
||||||
player: 0
|
player: 0
|
||||||
14
src/device/input/parse/v01/transform.go
Normal file
14
src/device/input/parse/v01/transform.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
type (
|
||||||
|
transformation map[string]string
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t transformation) 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
|
||||||
|
}
|
||||||
73
src/device/input/parse/v01/tts.go
Normal file
73
src/device/input/parse/v01/tts.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/faiface/beep"
|
||||||
|
"github.com/faiface/beep/effects"
|
||||||
|
"github.com/faiface/beep/speaker"
|
||||||
|
"github.com/faiface/beep/wav"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ttsLock = &sync.RWMutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v01 *V01) tts(text string) {
|
||||||
|
if err := v01._tts(text); err != nil {
|
||||||
|
log.Printf("failed to tts: %s: %v", text, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) _tts(text string) error {
|
||||||
|
if v01.cfg.Feedback.TTSURL == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := url.Parse(v01.cfg.Feedback.TTSURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(url.Path) < 2 {
|
||||||
|
url.Path = "/api/tts"
|
||||||
|
}
|
||||||
|
q := url.Query()
|
||||||
|
if q.Get("voice") == "" {
|
||||||
|
q.Set("voice", "en-us/glados-glow_tts")
|
||||||
|
}
|
||||||
|
if q.Get("lengthScale") == "" {
|
||||||
|
q.Set("lengthScale", "1")
|
||||||
|
}
|
||||||
|
q.Set("text", text)
|
||||||
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
resp, err := http.Get(url.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
b, _ := io.ReadAll(resp.Body)
|
||||||
|
if resp.StatusCode != http.StatusOK || resp.Header.Get("Content-Type") != "audio/wav" {
|
||||||
|
return fmt.Errorf("failed to call ttsurl: (%d) %s", resp.StatusCode, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, format, err := wav.Decode(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ttsLock.Lock()
|
||||||
|
defer ttsLock.Unlock()
|
||||||
|
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/30))
|
||||||
|
speaker.Play(&effects.Volume{Streamer: beep.ResampleRatio(4, 1, &beep.Ctrl{Streamer: beep.Loop(1, decoder)})})
|
||||||
|
duration := time.Duration(decoder.Len()) * format.SampleRate.D(1)
|
||||||
|
time.Sleep(duration)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
86
src/device/input/parse/v01/v01.go
Normal file
86
src/device/input/parse/v01/v01.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package v01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FlagDebug = os.Getenv("DEBUG") == "true"
|
||||||
|
FlagParseV01Config = os.Getenv("PARSE_V01_CONFIG")
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
V01 struct {
|
||||||
|
ctx context.Context
|
||||||
|
can context.CancelFunc
|
||||||
|
src raw.Raw
|
||||||
|
cfg config
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewV01(ctx context.Context, src raw.Raw) *V01 {
|
||||||
|
var cfg config
|
||||||
|
b, _ := ioutil.ReadFile(FlagParseV01Config)
|
||||||
|
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) CloseWrap() raw.Raw {
|
||||||
|
v01.can()
|
||||||
|
return v01.src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) Close() {
|
||||||
|
v01.can()
|
||||||
|
v01.src.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) Read() []button.Button {
|
||||||
|
line := v01.src.Read()
|
||||||
|
var msg message
|
||||||
|
if err := json.Unmarshal(line, &msg); err != nil {
|
||||||
|
log.Printf("%v: %s", err, line)
|
||||||
|
}
|
||||||
|
v01.telemetry(msg)
|
||||||
|
|
||||||
|
return v01.transform(msg).buttons()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) telemetry(msg message) {
|
||||||
|
if FlagDebug {
|
||||||
|
log.Printf("%s|%dms", msg.U, time.Now().UnixNano()/int64(time.Millisecond)-msg.T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v01 *V01) transform(msg message) message {
|
||||||
|
if len(v01.cfg.Players) == 0 {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
user := v01.cfg.Users[msg.U]
|
||||||
|
if user.Player < 1 {
|
||||||
|
msg.Y = ""
|
||||||
|
msg.N = ""
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
player := v01.cfg.Players[user.Player-1]
|
||||||
|
msg.Y = player.Transformation.pipe(msg.Y)
|
||||||
|
msg.N = player.Transformation.pipe(msg.N)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
package button_test
|
package v01_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
v01 "mayhem-party/src/device/input/parse/v01"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -15,7 +17,7 @@ import (
|
|||||||
func TestV01(t *testing.T) {
|
func TestV01(t *testing.T) {
|
||||||
src := constSrc(fmt.Sprintf(`{"T":%v,"U":"bel","Y":"abc","N":"cde"}`, time.Now().UnixNano()/int64(time.Millisecond)-50))
|
src := constSrc(fmt.Sprintf(`{"T":%v,"U":"bel","Y":"abc","N":"cde"}`, time.Now().UnixNano()/int64(time.Millisecond)-50))
|
||||||
t.Logf("(%v) %s", len(src), src.Read())
|
t.Logf("(%v) %s", len(src), src.Read())
|
||||||
v01 := button.NewV01(context.Background(), src)
|
v01 := v01.NewV01(context.Background(), src)
|
||||||
defer v01.Close()
|
defer v01.Close()
|
||||||
got := v01.Read()
|
got := v01.Read()
|
||||||
want := []button.Button{
|
want := []button.Button{
|
||||||
@@ -49,10 +51,10 @@ func TestV01WithCfg(t *testing.T) {
|
|||||||
- transformation:
|
- transformation:
|
||||||
w: i
|
w: i
|
||||||
`), os.ModePerm)
|
`), os.ModePerm)
|
||||||
button.FlagButtonV01Config = p
|
v01.FlagParseV01Config = p
|
||||||
|
|
||||||
t.Run("unknown user ignored", func(t *testing.T) {
|
t.Run("unknown user ignored", func(t *testing.T) {
|
||||||
v01 := button.NewV01(context.Background(), constSrc(`{"U":"qt","Y":"w"}`))
|
v01 := v01.NewV01(context.Background(), constSrc(`{"U":"qt","Y":"w"}`))
|
||||||
defer v01.Close()
|
defer v01.Close()
|
||||||
got := v01.Read()
|
got := v01.Read()
|
||||||
if len(got) != 0 {
|
if len(got) != 0 {
|
||||||
@@ -61,7 +63,7 @@ func TestV01WithCfg(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("player2", func(t *testing.T) {
|
t.Run("player2", func(t *testing.T) {
|
||||||
v01 := button.NewV01(context.Background(), constSrc(`{"U":"bel","Y":"w","N":"w"}`))
|
v01 := v01.NewV01(context.Background(), constSrc(`{"U":"bel","Y":"w","N":"w"}`))
|
||||||
defer v01.Close()
|
defer v01.Close()
|
||||||
got := v01.Read()
|
got := v01.Read()
|
||||||
if len(got) != 2 {
|
if len(got) != 2 {
|
||||||
@@ -82,6 +84,7 @@ func TestV01Feedback(t *testing.T) {
|
|||||||
os.WriteFile(p, []byte(`
|
os.WriteFile(p, []byte(`
|
||||||
feedback:
|
feedback:
|
||||||
addr: :27071
|
addr: :27071
|
||||||
|
ttsurl: http://localhost:15002
|
||||||
users:
|
users:
|
||||||
bel:
|
bel:
|
||||||
player: 2
|
player: 2
|
||||||
@@ -94,11 +97,11 @@ func TestV01Feedback(t *testing.T) {
|
|||||||
- transformation:
|
- transformation:
|
||||||
w: i
|
w: i
|
||||||
`), os.ModePerm)
|
`), os.ModePerm)
|
||||||
button.FlagButtonV01Config = p
|
v01.FlagParseV01Config = p
|
||||||
ctx, can := context.WithCancel(context.Background())
|
ctx, can := context.WithCancel(context.Background())
|
||||||
defer can()
|
defer can()
|
||||||
|
|
||||||
v01 := button.NewV01(ctx, constSrc(`{"U":"qt","Y":"w"}`))
|
v01 := v01.NewV01(ctx, constSrc(`{"U":"qt","Y":"w"}`))
|
||||||
defer v01.Close()
|
defer v01.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -134,4 +137,54 @@ func TestV01Feedback(t *testing.T) {
|
|||||||
t.Error(b)
|
t.Error(b)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("change broadcast", func(t *testing.T) {
|
||||||
|
want := `my new broadcast`
|
||||||
|
r, _ := http.NewRequest(http.MethodPut, "http://localhost:27071/broadcast", strings.NewReader(want))
|
||||||
|
resp, err := http.DefaultClient.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
resp, err = http.Get("http://localhost:27071")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
b, _ := io.ReadAll(resp.Body)
|
||||||
|
if string(b) != want {
|
||||||
|
t.Error(string(b))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("tts", func(t *testing.T) {
|
||||||
|
if os.Getenv("INTEGRATION_TTS") != "true" {
|
||||||
|
t.Skip("$INTEGRATION_TTS is not true")
|
||||||
|
}
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
resp, err := http.Get("http://localhost:27071/?say=hello%20world")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 2500)
|
||||||
|
r, _ := http.NewRequest(http.MethodGet, "http://localhost:27071", nil)
|
||||||
|
r.Header.Set("say", "No, HTTP does not define any limit. However most web servers do limit size of headers they accept. For example in Apache default limit is 8KB, in IIS it's 16K. Server will return 413 Entity Too Large error if headers size exceeds that limit.")
|
||||||
|
resp, err := http.DefaultClient.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
time.Sleep(time.Millisecond * 8500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type constSrc string
|
||||||
|
|
||||||
|
func (c constSrc) Close() {}
|
||||||
|
|
||||||
|
func (c constSrc) Read() []byte {
|
||||||
|
return []byte(c)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package button
|
package v01
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -42,7 +42,7 @@ func TestV01TransformationPipe(t *testing.T) {
|
|||||||
for name, d := range cases {
|
for name, d := range cases {
|
||||||
c := d
|
c := d
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
got := v01Transformation(c.xform).pipe(c.input)
|
got := transformation(c.xform).pipe(c.input)
|
||||||
if got != c.want {
|
if got != c.want {
|
||||||
t.Errorf("%+v(%s) want %s got %s", c.xform, c.input, c.want, got)
|
t.Errorf("%+v(%s) want %s got %s", c.xform, c.input, c.want, got)
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
chSigUsr1 = func() chan os.Signal {
|
ChSigUsr1 = func() chan os.Signal {
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, syscall.SIGUSR1)
|
signal.Notify(c, syscall.SIGUSR1)
|
||||||
return c
|
return c
|
||||||
@@ -24,7 +24,7 @@ type Refresh struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewRefresh(ctx context.Context, newWrap func() Wrap) *Refresh {
|
func NewRefresh(ctx context.Context, newWrap func() Wrap) *Refresh {
|
||||||
return NewRefreshWith(ctx, newWrap, chSigUsr1)
|
return NewRefreshWith(ctx, newWrap, ChSigUsr1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRefreshWith(ctx context.Context, newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
func NewRefreshWith(ctx context.Context, newWrap func() Wrap, ch <-chan os.Signal) *Refresh {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package wrap
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayhem-party/src/device/input/button"
|
"mayhem-party/src/device/input/button"
|
||||||
|
"mayhem-party/src/device/input/raw"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -50,7 +51,7 @@ func TestRefreshDoesntCloseSources(t *testing.T) {
|
|||||||
newParsers := 0
|
newParsers := 0
|
||||||
newParser := func() Wrap {
|
newParser := func() Wrap {
|
||||||
newParsers += 1
|
newParsers += 1
|
||||||
return button.NewPlaintext(src)
|
return src
|
||||||
}
|
}
|
||||||
ctx, can := context.WithCancel(context.Background())
|
ctx, can := context.WithCancel(context.Background())
|
||||||
defer can()
|
defer can()
|
||||||
@@ -69,10 +70,10 @@ func TestRefreshDoesntCloseSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
chSigUsr1 <- syscall.SIGINT
|
ChSigUsr1 <- syscall.SIGINT
|
||||||
}
|
}
|
||||||
time.Sleep(time.Millisecond * 250)
|
time.Sleep(time.Millisecond * 250)
|
||||||
if want := (telemetrySrc{reads: 5}); *src != want {
|
if want := (telemetrySrc{reads: 5, closeWraps: 5}); *src != want {
|
||||||
t.Errorf("want %+v, got %+v", want, *src)
|
t.Errorf("want %+v, got %+v", want, *src)
|
||||||
} else if newParsers != 6 {
|
} else if newParsers != 6 {
|
||||||
t.Error(newParsers)
|
t.Error(newParsers)
|
||||||
@@ -80,15 +81,21 @@ func TestRefreshDoesntCloseSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type telemetrySrc struct {
|
type telemetrySrc struct {
|
||||||
closes int
|
closeWraps int
|
||||||
reads int
|
closes int
|
||||||
|
reads int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *telemetrySrc) CloseWrap() raw.Raw {
|
||||||
|
src.closeWraps += 1
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *telemetrySrc) Close() {
|
func (src *telemetrySrc) Close() {
|
||||||
src.closes += 1
|
src.closes += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *telemetrySrc) Read() []byte {
|
func (src *telemetrySrc) Read() []button.Button {
|
||||||
src.reads += 1
|
src.reads += 1
|
||||||
return []byte("foo")
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ type Wrap interface {
|
|||||||
CloseWrap() raw.Raw
|
CloseWrap() raw.Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, srcFunc func() button.Parser) Wrap {
|
func New(ctx context.Context, parserFunc func() Wrap) Wrap {
|
||||||
maker := func() Wrap {
|
maker := func() Wrap {
|
||||||
return srcFunc()
|
return parserFunc()
|
||||||
}
|
}
|
||||||
if FlagBuffered {
|
if FlagBuffered {
|
||||||
oldMaker := maker
|
oldMaker := maker
|
||||||
|
|||||||
50
todo.yaml
50
todo.yaml
@@ -1,19 +1,19 @@
|
|||||||
todo:
|
todo:
|
||||||
- v01cfg includes messages to send per client and exposes http server for it
|
- https via home.blapointe and rproxy
|
||||||
- send clients messages to display
|
- tts for when someone said the word via larynx docker + http.get + https://pkg.go.dev/github.com/faiface/beep@v1.1.0/wav
|
||||||
- input.MayhemParty as a logical wrapper from mod10 but then gotta translate back
|
- endpoint for v01 to start read-only mode so when hotword spoken, players are dcd
|
||||||
to char for keyboard things somewhere; space delimited?
|
without losing players; press a hotkey that is bound to dolphin emulator pause
|
||||||
- todo: rusty configs have "name" for each client
|
|
||||||
details: |
|
|
||||||
'if name == server_broadcasted_name { debug_print_in_gui(server_broadcasted_message) }'
|
|
||||||
- todo: rotation triggers
|
- todo: rotation triggers
|
||||||
subtasks:
|
subtasks:
|
||||||
- minigame end
|
- todo: stdin
|
||||||
- random word from cur wikipedia page
|
subtasks:
|
||||||
- each person has their own hotword
|
- minigame end
|
||||||
- only spectators have hotwords and must get a player to speak it
|
- todo: voice recognition of hotwords to vote who dun it
|
||||||
- tribunal to vote who said it
|
subtasks:
|
||||||
- we have 7 players oooooof
|
- random word from cur wikipedia page
|
||||||
|
- each person has their own hotword
|
||||||
|
- only spectators have hotwords and must get a player to speak it
|
||||||
|
- tribunal to vote who said it
|
||||||
scheduled: []
|
scheduled: []
|
||||||
done:
|
done:
|
||||||
- todo: sticky keyboard input mode for enable/disable explicitly
|
- todo: sticky keyboard input mode for enable/disable explicitly
|
||||||
@@ -54,3 +54,27 @@ done:
|
|||||||
ts: Sat Mar 25 09:12:43 MDT 2023
|
ts: Sat Mar 25 09:12:43 MDT 2023
|
||||||
- todo: v01cfg includes messages to send per client and exposes tcp server for it
|
- todo: v01cfg includes messages to send per client and exposes tcp server for it
|
||||||
ts: Sat Mar 25 10:09:06 MDT 2023
|
ts: Sat Mar 25 10:09:06 MDT 2023
|
||||||
|
- todo: v01cfg includes messages to send per client and exposes http server for it
|
||||||
|
ts: Sat Mar 25 11:28:29 MDT 2023
|
||||||
|
- todo: send clients messages to display
|
||||||
|
ts: Sat Mar 25 11:28:29 MDT 2023
|
||||||
|
- todo: input.MayhemParty as a logical wrapper from mod10 but then gotta translate
|
||||||
|
back to char for keyboard things somewhere; space delimited?
|
||||||
|
ts: Sat Mar 25 11:28:40 MDT 2023
|
||||||
|
- todo: rusty configs have "name" for each client
|
||||||
|
details: |
|
||||||
|
'if name == server_broadcasted_name { debug_print_in_gui(server_broadcasted_message) }'
|
||||||
|
ts: Sat Mar 25 11:28:40 MDT 2023
|
||||||
|
- todo: rotation triggers
|
||||||
|
subtasks:
|
||||||
|
- minigame end
|
||||||
|
- random word from cur wikipedia page
|
||||||
|
- each person has their own hotword
|
||||||
|
- only spectators have hotwords and must get a player to speak it
|
||||||
|
- tribunal to vote who said it
|
||||||
|
ts: Sat Mar 25 11:29:52 MDT 2023
|
||||||
|
- todo: we have 7 players oooooof
|
||||||
|
ts: Sat Mar 25 11:29:52 MDT 2023
|
||||||
|
- todo: endpoint for v01 to start read-only mode so when hotword spoken, players are
|
||||||
|
dcd without losing players
|
||||||
|
ts: Sat Mar 25 23:16:47 MDT 2023
|
||||||
|
|||||||
Reference in New Issue
Block a user