package app import ( "log" "regexp" "strings" "time" "vrc_osc_go/internal/config" ) var ( selfJoinPattern = regexp.MustCompile(`OnPlayerJoined\s+(.+?)\s+\(usr_[0-9a-fA-F-]+\)`) selfLeftPattern = regexp.MustCompile(`OnPlayerLeft\s+(.+?)\s+\(usr_[0-9a-fA-F-]+\)`) ) func startSelfMonitor(cfg *config.Config, state *discordMuteState) { if cfg.VrcLog.SelfName == "" { return } go func() { lastState := "" for { path, err := findLatestVrchatLog() if err != nil { time.Sleep(currentGUIRefreshInterval()) continue } text, err := readFileTail(path, vrchatLogTailBytes) if err != nil { time.Sleep(currentGUIRefreshInterval()) continue } liveCfg := cfg if loaded, err := config.Load(""); err == nil && loaded != nil { liveCfg = loaded } nextState := extractLatestSelfLogState(DecodeVRChatLog(text), liveCfg.VrcLog.SelfName) if nextState != "" && nextState != lastState { if nextState == "left" && liveCfg.GUI.AutoUnmuteOnSelfLeave { if err := unmuteDiscordIfMuted(state, "self_leave"); err != nil { log.Printf("self monitor discord unmute failed: %v", err) SetDiscordSource("self_monitor_error") } } else if nextState == "left" { log.Printf("self monitor detected leave; auto unmute disabled") } SetDiscordSource("self_monitor") lastState = nextState } time.Sleep(guiRefreshIntervalFromConfig(liveCfg)) } }() } func extractLatestSelfLogState(text, selfName string) string { if selfName == "" { return "" } state := "" for _, rawLine := range strings.Split(text, "\n") { if m := selfJoinPattern.FindStringSubmatch(rawLine); len(m) == 2 && strings.TrimSpace(m[1]) == selfName { state = "joined" continue } if m := selfLeftPattern.FindStringSubmatch(rawLine); len(m) == 2 && strings.TrimSpace(m[1]) == selfName { state = "left" } } return state }