Port
This commit is contained in:
@@ -18,6 +18,8 @@ from discord_control.actions import setDiscordMuted
|
||||
_monitor_thread = None
|
||||
_monitor_stop_event = threading.Event()
|
||||
_last_sent_output = None
|
||||
_last_join_leave_output = ""
|
||||
_monitor_vrc_state = None
|
||||
_self_monitor_thread = None
|
||||
_self_monitor_stop_event = threading.Event()
|
||||
_last_self_state = None
|
||||
@@ -39,7 +41,10 @@ ENTERING_ROOM_PATTERN = re.compile(
|
||||
r"\[Behaviour\]\s+(?:Entering Room|Joining or Creating Room):\s+(.+)$"
|
||||
)
|
||||
WORLD_NAME_PATTERN = re.compile(
|
||||
r"worldId=(wrld_[0-9a-fA-F-]+)(?::[^,}]*)?,\s*worldName=([^,}]+)"
|
||||
r"worldId=(wrld_[0-9a-fA-F-]+)(?::[^,}]*)?,\s*(?:instanceId=([^,}]+),\s*)?worldName=([^,}]+)"
|
||||
)
|
||||
WORLD_LOCATION_PATTERN = re.compile(
|
||||
r"worldId=(wrld_[0-9a-fA-F-]+)(?::[^,}]*)?,\s*instanceId=([^,}]+)"
|
||||
)
|
||||
TIME_PATTERN = re.compile(
|
||||
r"(\d{4}\.\d{2}\.\d{2}\s+\d{2}:\d{2}:\d{2})"
|
||||
@@ -77,7 +82,9 @@ param(
|
||||
$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)
|
||||
if ($Response.StatusCode -ne 200) {
|
||||
Write-Output ("STATUS=" + $Response.StatusCode)
|
||||
}
|
||||
'''
|
||||
|
||||
ps_path = None
|
||||
@@ -127,7 +134,7 @@ param(
|
||||
|
||||
stdout = (result.stdout or "").strip()
|
||||
if stdout:
|
||||
log("INFO", f"Discord webhook PowerShell stdout={stdout}")
|
||||
log("DEBUG", f"Discord webhook PowerShell stdout={stdout}")
|
||||
log("INFO", "Discord webhook sent via PowerShell")
|
||||
return True
|
||||
|
||||
@@ -205,7 +212,7 @@ def extractWorldNameMap(text):
|
||||
match = WORLD_NAME_PATTERN.search(raw_line)
|
||||
if match:
|
||||
world_id = match.group(1).strip()
|
||||
world_name = match.group(2).strip()
|
||||
world_name = match.group(3).strip()
|
||||
if world_id and world_name:
|
||||
world_name_map[world_id] = world_name
|
||||
continue
|
||||
@@ -322,10 +329,9 @@ def matchesAnyPattern(text, compiled_patterns):
|
||||
|
||||
|
||||
def formatJoinLeaveLine(line_time, action, name, state, guest_names):
|
||||
staff_count = len(state["staff_present_set"])
|
||||
member_count = len(state["member_present_set"])
|
||||
total_count = len(state["present_set"])
|
||||
guest_count = len(state["guest_names"])
|
||||
return f'[{formatClock(line_time)}][{action}] {name} {member_count - staff_count}/{guest_count}'
|
||||
return f'[{formatClock(line_time)}][{action}] {name} {total_count}/{guest_count}'
|
||||
|
||||
|
||||
def parseVrchatEvents(text, config):
|
||||
@@ -340,12 +346,11 @@ def parseVrchatEvents(text, config):
|
||||
"location": "",
|
||||
"world_id": "",
|
||||
"instance_id": "",
|
||||
"present_set": set(),
|
||||
"guest_names": guest_names,
|
||||
"guest_name_set": set(guest_names),
|
||||
"staff_names": staff_names,
|
||||
"guest_present_set": set(),
|
||||
"staff_present_set": set(),
|
||||
"member_present_set": set(),
|
||||
"missing_count": config["missing_count"],
|
||||
"last_notice_key": None,
|
||||
}
|
||||
@@ -383,9 +388,8 @@ def parseVrchatEvents(text, config):
|
||||
state["location"] = location or world_id
|
||||
state["world_id"] = world_id
|
||||
state["instance_id"] = instance_id
|
||||
state["present_set"].clear()
|
||||
state["guest_present_set"].clear()
|
||||
state["staff_present_set"].clear()
|
||||
state["member_present_set"].clear()
|
||||
state["last_notice_key"] = None
|
||||
output_lines = []
|
||||
world_label = formatWorldLabel(world_id, world_name_map)
|
||||
@@ -398,7 +402,7 @@ def parseVrchatEvents(text, config):
|
||||
join_leave_line = None
|
||||
if isSelf(name, self_name):
|
||||
self_state = "joined"
|
||||
state["member_present_set"].add(name)
|
||||
state["present_set"].add(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
continue
|
||||
@@ -407,16 +411,16 @@ def parseVrchatEvents(text, config):
|
||||
|
||||
if role == "guest":
|
||||
state["guest_present_set"].add(name)
|
||||
state["member_present_set"].add(name)
|
||||
state["present_set"].add(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
appendNoticeIfNeeded(output_lines, state)
|
||||
elif role == "staff":
|
||||
state["staff_present_set"].add(name)
|
||||
state["present_set"].add(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
else:
|
||||
state["member_present_set"].add(name)
|
||||
state["present_set"].add(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
continue
|
||||
@@ -427,7 +431,7 @@ def parseVrchatEvents(text, config):
|
||||
join_leave_line = None
|
||||
if isSelf(name, self_name):
|
||||
self_state = "left"
|
||||
state["member_present_set"].discard(name)
|
||||
state["present_set"].discard(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
continue
|
||||
@@ -436,16 +440,16 @@ def parseVrchatEvents(text, config):
|
||||
|
||||
if role == "guest":
|
||||
state["guest_present_set"].discard(name)
|
||||
state["member_present_set"].discard(name)
|
||||
state["present_set"].discard(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
appendNoticeIfNeeded(output_lines, state)
|
||||
elif role == "staff":
|
||||
state["staff_present_set"].discard(name)
|
||||
state["present_set"].discard(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
else:
|
||||
state["member_present_set"].discard(name)
|
||||
state["present_set"].discard(name)
|
||||
join_leave_line = formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
output_lines.append(join_leave_line)
|
||||
continue
|
||||
@@ -453,6 +457,142 @@ def parseVrchatEvents(text, config):
|
||||
return output_lines, self_state
|
||||
|
||||
|
||||
def createVrcState(config, world_name_map=None):
|
||||
guest_names = config["guest_names"]
|
||||
return {
|
||||
"location": "",
|
||||
"world_id": "",
|
||||
"instance_id": "",
|
||||
"present_set": set(),
|
||||
"guest_names": guest_names,
|
||||
"guest_name_set": set(guest_names),
|
||||
"staff_names": set(config["staff_names"]),
|
||||
"guest_present_set": set(),
|
||||
"missing_count": config["missing_count"],
|
||||
"last_notice_key": None,
|
||||
"world_name_map": world_name_map or {},
|
||||
"last_join_leave_output": "",
|
||||
}
|
||||
|
||||
|
||||
def processVrcText(text, config, state):
|
||||
return processVrcLines(text.splitlines(), config, state)
|
||||
|
||||
|
||||
def processVrcLines(raw_lines, config, state):
|
||||
output_lines = []
|
||||
world_name_map = state.get("world_name_map") or {}
|
||||
guest_names = state["guest_names"]
|
||||
staff_names = state["staff_names"]
|
||||
self_name = config.get("self_name", "")
|
||||
compiled_patterns = compileLogPatterns(config.get("log_patterns", []))
|
||||
|
||||
for raw_line in raw_lines:
|
||||
line_time = parseLineTime(raw_line)
|
||||
if not line_time:
|
||||
continue
|
||||
|
||||
line_matches = matchesAnyPattern(raw_line, compiled_patterns)
|
||||
|
||||
room_match = ENTERING_ROOM_PATTERN.search(raw_line)
|
||||
world_name_match = WORLD_NAME_PATTERN.search(raw_line)
|
||||
world_location_match = WORLD_LOCATION_PATTERN.search(raw_line)
|
||||
world_match = WORLD_PATTERN.search(raw_line)
|
||||
location = ""
|
||||
world_id = ""
|
||||
instance_id = ""
|
||||
|
||||
if world_name_match:
|
||||
world_id = world_name_match.group(1).strip()
|
||||
instance_id = (world_name_match.group(2) or "").strip()
|
||||
location = world_id
|
||||
elif world_location_match:
|
||||
world_id = world_location_match.group(1).strip()
|
||||
instance_id = world_location_match.group(2).strip()
|
||||
location = f"{world_id}:{instance_id}" if instance_id else world_id
|
||||
elif room_match and world_match:
|
||||
location = world_match.group(1).strip()
|
||||
world_id, instance_id = splitWorldLocation(location)
|
||||
elif world_match:
|
||||
location = world_match.group(1).strip()
|
||||
world_id, instance_id = splitWorldLocation(location)
|
||||
|
||||
next_location = f"{world_id}:{instance_id}" if world_id else ""
|
||||
if next_location and next_location != state["location"]:
|
||||
state["location"] = next_location
|
||||
state["world_id"] = world_id
|
||||
state["instance_id"] = instance_id
|
||||
state["present_set"].clear()
|
||||
state["guest_present_set"].clear()
|
||||
state["last_notice_key"] = None
|
||||
state["last_join_leave_output"] = ""
|
||||
world_label = formatWorldLabel(world_id, world_name_map)
|
||||
if world_label != "不明":
|
||||
output_lines.append(f"ワールド入室: {world_label}")
|
||||
|
||||
join_match = JOIN_PATTERN.search(raw_line)
|
||||
if join_match and line_matches:
|
||||
name = join_match.group(1).strip()
|
||||
if isSelf(name, self_name):
|
||||
state["present_set"].add(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
)
|
||||
continue
|
||||
|
||||
role = classifyUser(name, state["guest_name_set"], staff_names)
|
||||
if role == "guest":
|
||||
state["guest_present_set"].add(name)
|
||||
state["present_set"].add(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
)
|
||||
appendNoticeIfNeeded(output_lines, state)
|
||||
elif role == "staff":
|
||||
state["present_set"].add(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
)
|
||||
else:
|
||||
state["present_set"].add(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "join", name, state, guest_names)
|
||||
)
|
||||
continue
|
||||
|
||||
left_match = LEFT_PATTERN.search(raw_line)
|
||||
if left_match and line_matches:
|
||||
name = left_match.group(1).strip()
|
||||
if isSelf(name, self_name):
|
||||
state["present_set"].discard(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
)
|
||||
continue
|
||||
|
||||
role = classifyUser(name, state["guest_name_set"], staff_names)
|
||||
if role == "guest":
|
||||
state["guest_present_set"].discard(name)
|
||||
state["present_set"].discard(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
)
|
||||
appendNoticeIfNeeded(output_lines, state)
|
||||
elif role == "staff":
|
||||
state["present_set"].discard(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
)
|
||||
else:
|
||||
state["present_set"].discard(name)
|
||||
output_lines.append(
|
||||
formatJoinLeaveLine(line_time, "leave", name, state, guest_names)
|
||||
)
|
||||
continue
|
||||
|
||||
return output_lines, state
|
||||
|
||||
|
||||
def extractLatestSelfLogState(text, self_name):
|
||||
if not self_name:
|
||||
return None
|
||||
@@ -481,7 +621,9 @@ def collectVrchatLog(pattern=None, notify_changed=True):
|
||||
latest_log = findLatestVrchatLog()
|
||||
|
||||
text = readTextSafe(latest_log)
|
||||
lines, self_state = parseVrchatEvents(text, config)
|
||||
world_name_map = extractWorldNameMap(text)
|
||||
state = createVrcState(config, world_name_map=world_name_map)
|
||||
lines, state = processVrcText(text, config, state)
|
||||
|
||||
output_text = "\n".join(lines) if lines else ""
|
||||
output_path = None
|
||||
@@ -489,13 +631,22 @@ def collectVrchatLog(pattern=None, notify_changed=True):
|
||||
output_path = appendRuntimeLog("VRC LOG", output_text)
|
||||
join_leave_lines = [line for line in lines if "[join]" in line or "[leave]" in line]
|
||||
if join_leave_lines:
|
||||
appendJoinLeaveLog("\n".join(join_leave_lines))
|
||||
global _last_join_leave_output
|
||||
current_join_leave_output = "\n".join(join_leave_lines)
|
||||
join_leave_diff = buildDiffMessage(
|
||||
_last_join_leave_output,
|
||||
current_join_leave_output,
|
||||
)
|
||||
if join_leave_diff:
|
||||
appendJoinLeaveLog(join_leave_diff)
|
||||
_last_join_leave_output = current_join_leave_output
|
||||
|
||||
if notify_changed:
|
||||
global _last_sent_output
|
||||
diff_text = buildDiffMessage(_last_sent_output, output_text)
|
||||
if diff_text:
|
||||
try:
|
||||
appendRuntimeLog("VRC LOG DIFF", diff_text)
|
||||
ok = sendWebhook(diff_text)
|
||||
if ok:
|
||||
log("INFO", "Discord webhook diff sent")
|
||||
@@ -512,6 +663,16 @@ def collectVrchatLog(pattern=None, notify_changed=True):
|
||||
return output_path
|
||||
|
||||
|
||||
def initializeVrcMonitorState():
|
||||
global _monitor_vrc_state
|
||||
config = loadVrcLogConfig()
|
||||
latest_log = findLatestVrchatLog()
|
||||
text = readTextSafe(latest_log)
|
||||
world_name_map = extractWorldNameMap(text)
|
||||
_monitor_vrc_state = createVrcState(config, world_name_map=world_name_map)
|
||||
return _monitor_vrc_state
|
||||
|
||||
|
||||
def _readNewText(path, offset):
|
||||
for encoding in ("utf-8", "utf-8-sig", "cp932", "shift_jis"):
|
||||
try:
|
||||
@@ -561,11 +722,54 @@ def _watchVrchatLogChanges(stop_event, label, on_relevant_change):
|
||||
|
||||
|
||||
def _monitor_loop():
|
||||
global _monitor_vrc_state
|
||||
|
||||
if _monitor_vrc_state is None:
|
||||
try:
|
||||
initializeVrcMonitorState()
|
||||
except Exception as e:
|
||||
log("ERROR", f"VRC log monitor init failed detail={e}")
|
||||
|
||||
def on_relevant_change(text):
|
||||
global _monitor_vrc_state
|
||||
global _last_sent_output
|
||||
global _last_join_leave_output
|
||||
|
||||
if not any(token in text for token in ("OnPlayerJoined", "OnPlayerLeft", "Entering Room", "Joining or Creating Room", "worldId=", "wrld_")):
|
||||
return
|
||||
|
||||
collectVrchatLog(notify_changed=True)
|
||||
try:
|
||||
config = loadVrcLogConfig()
|
||||
if _monitor_vrc_state is None:
|
||||
initializeVrcMonitorState()
|
||||
lines, state = processVrcText(text, config, _monitor_vrc_state)
|
||||
_monitor_vrc_state = state
|
||||
output_text = "\n".join(lines) if lines else ""
|
||||
if not output_text:
|
||||
return
|
||||
appendRuntimeLog("VRC LOG", output_text)
|
||||
join_leave_lines = [line for line in lines if "[join]" in line or "[leave]" in line]
|
||||
if join_leave_lines:
|
||||
current_join_leave_output = "\n".join(join_leave_lines)
|
||||
if current_join_leave_output != _last_join_leave_output:
|
||||
join_leave_diff = buildDiffMessage(_last_join_leave_output, current_join_leave_output)
|
||||
if join_leave_diff:
|
||||
appendJoinLeaveLog(join_leave_diff)
|
||||
_last_join_leave_output = current_join_leave_output
|
||||
global _last_sent_output
|
||||
diff_text = buildDiffMessage(_last_sent_output, output_text)
|
||||
if diff_text:
|
||||
appendRuntimeLog("VRC LOG DIFF", diff_text)
|
||||
ok = sendWebhook(diff_text)
|
||||
if ok:
|
||||
log("INFO", "Discord webhook diff sent")
|
||||
_last_sent_output = output_text
|
||||
else:
|
||||
log("ERROR", "Discord webhook diff send failed")
|
||||
for line in lines:
|
||||
print(line, flush=True)
|
||||
except Exception as e:
|
||||
log("ERROR", f"VRC log monitor chunk failed detail={e}")
|
||||
|
||||
_watchVrchatLogChanges(
|
||||
_monitor_stop_event,
|
||||
|
||||
Reference in New Issue
Block a user