diff --git a/doc/ExMenu1.png b/doc/ExMenu1.png new file mode 100644 index 0000000..b1b023d Binary files /dev/null and b/doc/ExMenu1.png differ diff --git a/doc/ExMenu2.png b/doc/ExMenu2.png new file mode 100644 index 0000000..6c1403e Binary files /dev/null and b/doc/ExMenu2.png differ diff --git a/doc/VRC_Log.png b/doc/VRC_Log.png new file mode 100644 index 0000000..3e2d007 Binary files /dev/null and b/doc/VRC_Log.png differ diff --git a/src/vrc_log/log_actions.py b/src/vrc_log/log_actions.py index c70a385..2ea7047 100644 --- a/src/vrc_log/log_actions.py +++ b/src/vrc_log/log_actions.py @@ -73,10 +73,11 @@ param( [string]$Message ) -[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -$Payload = @{ content = $Message } | ConvertTo-Json -Compress -$Body = [System.Text.Encoding]::UTF8.GetBytes($Payload) -Invoke-WebRequest -Uri $WebhookUrl -Method Post -ContentType 'application/json; charset=utf-8' -Body $Body -UseBasicParsing | Out-Null + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + $Payload = @{ content = $Message } | ConvertTo-Json -Compress + $Body = [System.Text.Encoding]::UTF8.GetBytes($Payload) + $Response = Invoke-WebRequest -Uri $WebhookUrl -Method Post -ContentType 'application/json; charset=utf-8' -Body $Body -UseBasicParsing + Write-Output ("STATUS=" + $Response.StatusCode) ''' ps_path = None @@ -124,6 +125,9 @@ Invoke-WebRequest -Uri $WebhookUrl -Method Post -ContentType 'application/json; log("ERROR", f"Discord webhook PowerShell failed code={result.returncode} stderr={stderr} stdout={stdout}") return False + stdout = (result.stdout or "").strip() + if stdout: + log("INFO", f"Discord webhook PowerShell stdout={stdout}") log("INFO", "Discord webhook sent via PowerShell") return True @@ -274,6 +278,17 @@ def appendNoticeIfNeeded(output_lines, state): state["last_notice_key"] = notice_key +def debug(message): + log("DEBUG", message) + + +def formatJoinLeaveLine(line_time, action, name, state, guest_names): + staff_count = len(state["staff_present_set"]) + member_count = len(state["member_present_set"]) + guest_count = len(state["guest_names"]) + return f'[{formatClock(line_time)}][{action}] {name} {member_count - staff_count}/{guest_count}' + + def parseVrchatEvents(text, config): start_time = getLogStartTime(datetime.now()) guest_names = config["guest_names"] @@ -316,8 +331,10 @@ def parseVrchatEvents(text, config): state["member_present_set"].clear() state["last_notice_key"] = None output_lines = [] - output_lines.append(f"ワールド入室: {formatWorldLabel(world_id, world_name_map)}") - output_lines.append(f"現在Guest: 0/{len(guest_names)}") + world_label = formatWorldLabel(world_id, world_name_map) + if world_label != "不明": + output_lines.append(f"ワールド入室: {world_label}") + debug(f"world changed world_id={world_id} label={world_label}") join_match = JOIN_PATTERN.search(raw_line) if join_match and state["world_id"]: @@ -325,9 +342,7 @@ def parseVrchatEvents(text, config): if isSelf(name, self_name): self_state = "joined" state["member_present_set"].add(name) - output_lines.append( - f'[{formatClock(line_time)}] [join] [self] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "join", name, state, guest_names)) continue role = classifyUser(name, state["guest_name_set"], state["staff_names"]) @@ -335,20 +350,14 @@ def parseVrchatEvents(text, config): if role == "guest": state["guest_present_set"].add(name) state["member_present_set"].add(name) - output_lines.append( - f'[{formatClock(line_time)}] [join] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "join", name, state, guest_names)) appendNoticeIfNeeded(output_lines, state) elif role == "staff": state["staff_present_set"].add(name) - output_lines.append( - f'[{formatClock(line_time)}] [join] [staff] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "join", name, state, guest_names)) else: state["member_present_set"].add(name) - output_lines.append( - f'[{formatClock(line_time)}] [join] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "join", name, state, guest_names)) continue left_match = LEFT_PATTERN.search(raw_line) @@ -357,9 +366,7 @@ def parseVrchatEvents(text, config): if isSelf(name, self_name): self_state = "left" state["member_present_set"].discard(name) - output_lines.append( - f'[{formatClock(line_time)}] [leave] [self] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "leave", name, state, guest_names)) continue role = classifyUser(name, state["guest_name_set"], state["staff_names"]) @@ -367,20 +374,14 @@ def parseVrchatEvents(text, config): if role == "guest": state["guest_present_set"].discard(name) state["member_present_set"].discard(name) - output_lines.append( - f'[{formatClock(line_time)}] [leave] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "leave", name, state, guest_names)) appendNoticeIfNeeded(output_lines, state) elif role == "staff": state["staff_present_set"].discard(name) - output_lines.append( - f'[{formatClock(line_time)}] [leave] [staff] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "leave", name, state, guest_names)) else: state["member_present_set"].discard(name) - output_lines.append( - f'[{formatClock(line_time)}] [leave] {name} {len(state["member_present_set"])}/{len(guest_names)}' - ) + output_lines.append(formatJoinLeaveLine(line_time, "leave", name, state, guest_names)) return output_lines, self_state @@ -417,12 +418,17 @@ def collectVrchatLog(pattern=None, notify_changed=True): text = readTextSafe(latest_log) lines, self_state = parseVrchatEvents(text, config) + has_join_leave = any("[join]" in line or "[leave]" in line for line in lines) + if has_join_leave: + lines = ["[DEBUG] 入退出イベント検出"] + lines + output_text = "\n".join(lines) if lines else "[INFO] 対象イベントなし" output_path = appendRuntimeLog("VRC LOG", output_text) if notify_changed: global _last_sent_output - if output_text != _last_sent_output: + should_send = output_text != _last_sent_output or has_join_leave + if should_send: try: ok = sendWebhook(output_text) if ok: @@ -434,8 +440,11 @@ def collectVrchatLog(pattern=None, notify_changed=True): log("ERROR", f"Discord webhook failed detail={e}") log("INFO", f"result appended={output_path}") - for line in lines or ["[INFO] 対象イベントなし"]: - print(line, flush=True) + if lines: + for line in lines: + print(line, flush=True) + else: + debug("対象イベントなし") return output_path @@ -479,9 +488,7 @@ def _watchVrchatLogChanges(stop_event, label, on_relevant_change): if latest_size > offset: text, offset = _readNewText(latest_log, offset) - - if RELEVANT_LOG_PATTERN.search(text): - on_relevant_change(text) + on_relevant_change(text) except Exception as e: log("ERROR", f"{label} realtime watcher failed detail={e}") @@ -491,7 +498,10 @@ def _watchVrchatLogChanges(stop_event, label, on_relevant_change): def _monitor_loop(): - def on_relevant_change(_text): + def on_relevant_change(text): + preview = " ".join(text.splitlines()[:3]).strip() + if preview: + debug(f"VRC log monitor change preview={preview[:180]}") collectVrchatLog(notify_changed=True) _watchVrchatLogChanges(