package app import ( "log" "os" "os/signal" "sync" "syscall" "time" "vrc_osc_go/internal/config" "vrc_osc_go/internal/consenttool" "vrc_osc_go/internal/osc" ) type discordMuteState struct { mu sync.Mutex muted bool buttonPressed bool lastAction time.Time } func Run(configPath string, mode string) error { if mode != "" { return consenttool.Run(configPath, mode) } cfg, err := config.Load(configPath) if err != nil { return err } InitGuestTracker(NewGuestTracker(cfg.VrcLog.GuestNames)) log.Printf("vrc_osc_go starting osc=%s:%d config=%s", cfg.OSC.Host, cfg.OSC.Port, configPath) server := osc.NewServer(cfg.OSC.Host, cfg.OSC.Port) discordState := &discordMuteState{muted: true} SetDiscordMuted(true) server.Map("DiscordSend", func(_ string, args []osc.Value) error { log.Printf("received DiscordSend args=%+v", args) if err := handleDiscordSend(discordState, args); err != nil { log.Printf("discord mute failed: %v", err) return err } SetDiscordMuted(discordState.muted) return nil }) server.Map("ocrEnabled", func(_ string, args []osc.Value) error { log.Printf("received ocrEnabled args=%+v", args) if err := runPythonOcrFromScreen(); err != nil { log.Printf("ocr failed: %v", err) return err } return nil }) errCh := make(chan error, 1) go func() { log.Printf("osc server listening on %s:%d", cfg.OSC.Host, cfg.OSC.Port) errCh <- server.Serve() }() go watchVrchatLog() startSelfMonitor(cfg, discordState) sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) select { case err := <-errCh: log.Printf("osc server stopped: %v", err) return err case <-sigCh: log.Printf("shutdown signal received") server.Close() return nil } }