diff --git a/bus/bus.go b/bus/bus.go new file mode 100644 index 0000000..eb04ac4 --- /dev/null +++ b/bus/bus.go @@ -0,0 +1,49 @@ +package bus + +import ( + "time" + + "git.t-juice.club/torjus/ghettoptt/metrics" + "github.com/godbus/dbus/v5" +) + +type MumbleBus struct { + conn *dbus.Conn + obj dbus.BusObject + metrics *metrics.MumbleMetricsCollector + + lastTalked time.Time +} + +func NewMumbleBus(m *metrics.MumbleMetricsCollector) *MumbleBus { + mb := &MumbleBus{ + metrics: m, + } + conn, err := dbus.ConnectSessionBus() + if err != nil { + panic(err) + } + mb.obj = conn.Object("net.sourceforge.mumble.mumble", "/") + mb.conn = conn + return mb +} + +func (m *MumbleBus) Close() error { + return m.conn.Close() +} + +func (m *MumbleBus) StartTalking() error { + m.obj.Call("net.sourceforge.mumble.Mumble.startTalking", 0) + m.lastTalked = time.Now() + m.metrics.PTTTogglesTotal.Add(1) + return nil +} + +func (m *MumbleBus) StopTalking() error { + m.obj.Call("net.sourceforge.mumble.Mumble.stopTalking", 0) + duration := time.Since(m.lastTalked) + m.lastTalked = time.Time{} + m.metrics.TalkSecondsTotal.Add(duration.Seconds()) + m.metrics.PTTTogglesTotal.Add(1) + return nil +} diff --git a/flake.nix b/flake.nix index e798358..42237bf 100644 --- a/flake.nix +++ b/flake.nix @@ -24,7 +24,7 @@ default = pkgs.buildGoModule { name = "ghettoptt"; src = ./.; - vendorHash = "sha256-S/t8EkMRjceGhNcNqGhmnMviQrhqt7n9FiCmpxbKUoI="; + vendorHash = "sha256-WhlifAFIqoBNhBWGmYgHc5DWmdhHnK/2kYbEF/DOclw="; }; }); devShells = forAllSystems ({ pkgs }: { diff --git a/go.mod b/go.mod index 9305699..ea7f588 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,17 @@ module git.t-juice.club/torjus/ghettoptt go 1.21.6 require ( - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/holoplot/go-evdev v0.0.0-20240306072622-217e18f17db1 // indirect + github.com/godbus/dbus/v5 v5.1.0 + github.com/holoplot/go-evdev v0.0.0-20240306072622-217e18f17db1 + github.com/prometheus/client_golang v1.19.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + golang.org/x/sys v0.16.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect ) diff --git a/go.sum b/go.sum index 44fa5d2..7119073 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,24 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/holoplot/go-evdev v0.0.0-20240306072622-217e18f17db1 h1:92OsBIf5KB1Tatx+uUGOhah73jyNUrt7DmfDRXXJ5Xo= github.com/holoplot/go-evdev v0.0.0-20240306072622-217e18f17db1/go.mod h1:iHAf8OIncO2gcQ8XOjS7CMJ2aPbX2Bs0wl5pZyanEqk= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/main.go b/main.go index 51a18b5..ad2b69e 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,17 @@ import ( "context" "errors" "log/slog" + "net/http" "os" "os/signal" - "time" - "github.com/godbus/dbus/v5" + "git.t-juice.club/torjus/ghettoptt/bus" + "git.t-juice.club/torjus/ghettoptt/metrics" "github.com/holoplot/go-evdev" + "github.com/prometheus/client_golang/prometheus/promhttp" ) -const Version = "v0.1.1" +const Version = "v0.1.2" func main() { // Setup logger @@ -24,14 +26,11 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() - // Connect to dbus - conn, err := dbus.ConnectSessionBus() - if err != nil { - panic(err) - } - defer conn.Close() + // Setup metrics + m := metrics.NewMumbleMetricsCollector() - obj := conn.Object("net.sourceforge.mumble.mumble", "/") + mbus := bus.NewMumbleBus(m) + defer mbus.Close() // Start reading input events input, err := evdev.OpenWithFlags("/dev/input/event1", os.O_RDONLY) @@ -43,6 +42,26 @@ func main() { var done bool + // Start metrics server + srvCtx, srvCancel := context.WithCancel(context.Background()) + defer srvCancel() + go func() { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + srv := http.Server{ + Handler: mux, + Addr: ":8989", + } + go func() { + <-ctx.Done() + srv.Shutdown(context.Background()) + srvCancel() + }() + + slog.Info("Starting metrics server", "addr", srv.Addr) + srv.ListenAndServe() + }() + // Listen for context cancellation go func() { <-ctx.Done() @@ -50,7 +69,6 @@ func main() { input.Close() }() - var lastOn time.Time // Start listening for PTT key slog.Info("Starting event listener", "version", Version) for !done { @@ -59,20 +77,19 @@ func main() { if errors.Is(err, os.ErrClosed) { continue } - slog.Error("Error reading fron input device", "error", err) + slog.Error("Error reading from input device", "error", err) os.Exit(1) } if ev.Code == evdev.KEY_F24 && ev.Value == 1 { slog.Info("PTT ON") - lastOn = time.Now() - obj.Call("net.sourceforge.mumble.Mumble.startTalking", 0) + mbus.StartTalking() } if ev.Code == evdev.KEY_F24 && ev.Value == 0 { - speakDuration := time.Since(lastOn).String() - slog.Info("PTT OFF", "duration", speakDuration) - obj.Call("net.sourceforge.mumble.Mumble.stopTalking", 0) + slog.Info("PTT OFF") + mbus.StopTalking() } } - obj.Call("net.sourceforge.mumble.Mumble.stopTalking", 0) + mbus.StopTalking() + <-srvCtx.Done() slog.Info("Exiting") } diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 0000000..47ada10 --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,27 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +type MumbleMetricsCollector struct { + TalkSecondsTotal prometheus.Counter + PTTTogglesTotal prometheus.Counter +} + +func NewMumbleMetricsCollector() *MumbleMetricsCollector { + talkSecondsTotal := promauto.NewCounter(prometheus.CounterOpts{ + Name: "mumble_talk_seconds_total", + Help: "The total number of seconds a user has been talking", + }) + pttTogglesTotal := promauto.NewCounter(prometheus.CounterOpts{ + Name: "mumble_ptt_toggles_total", + Help: "The total number of seconds a user has been talking", + }) + + return &MumbleMetricsCollector{ + TalkSecondsTotal: talkSecondsTotal, + PTTTogglesTotal: pttTogglesTotal, + } +}