Initial VRC OSC commit

This commit is contained in:
every_holiday
2026-06-04 00:46:37 +09:00
commit 1e9a77b90d
22 changed files with 2831 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
from datetime import datetime
import ctypes
import threading
import time
import pyautogui
import pygetwindow as gw
BROWSER_KEYWORDS = [
"google chrome",
"microsoft edge",
"mozilla firefox",
]
_discord_muted = True
_discord_mute_lock = threading.Lock()
def log(level, message):
now = datetime.now().strftime("%H:%M:%S")
print(f"[{level}] {now} {message}", flush=True)
def findDiscordWindow():
candidates = []
for window in gw.getAllWindows():
title = (window.title or "").strip()
if not title:
continue
if "discord" not in title.lower():
continue
candidates.append(window)
if not candidates:
raise RuntimeError("Discordウィンドウが見つかりません")
candidates.sort(key=lambda window: -(window.width * window.height))
return candidates[0]
def findBrowserWindow():
candidates = []
for window in gw.getAllWindows():
title = (window.title or "").strip()
if not title:
continue
lowered = title.lower()
if not any(keyword in lowered for keyword in BROWSER_KEYWORDS):
continue
candidates.append(window)
if not candidates:
raise RuntimeError("ブラウザウィンドウが見つかりません")
candidates.sort(key=lambda window: -(window.width * window.height))
return candidates[0]
def findDiscordBrowserWindow():
candidates = []
for window in gw.getAllWindows():
title = (window.title or "").strip()
if not title:
continue
lowered = title.lower()
if "discord" not in lowered:
continue
if not any(keyword in lowered for keyword in BROWSER_KEYWORDS):
continue
candidates.append(window)
if not candidates:
return None
candidates.sort(key=lambda window: -(window.width * window.height))
return candidates[0]
def activateDiscordWindow():
window = findDiscordWindow()
hwnd = getattr(window, "_hWnd", None)
try:
if window.isMinimized:
window.restore()
except Exception:
pass
if hwnd:
try:
ctypes.windll.user32.ShowWindow(hwnd, 9)
except Exception:
pass
try:
window.activate()
except Exception as e:
log("ERROR", f"Discord activate() failed detail={e}")
if hwnd:
try:
ctypes.windll.user32.BringWindowToTop(hwnd)
ctypes.windll.user32.SetForegroundWindow(hwnd)
except Exception as e:
log("ERROR", f"Discord SetForegroundWindow failed detail={e}")
time.sleep(0.15)
log("INFO", f"Discord window title={window.title}")
return window
def findVrchatWindow():
candidates = []
for window in gw.getAllWindows():
title = (window.title or "").strip()
if not title:
continue
lowered = title.lower()
if lowered == "vrchat":
candidates.append((0, window))
continue
if lowered.startswith("vrchat "):
candidates.append((1, window))
continue
if lowered.startswith("vrchat"):
candidates.append((2, window))
if not candidates:
raise RuntimeError("VRChatウィンドウが見つかりません")
candidates.sort(key=lambda item: (item[0], -(item[1].width * item[1].height)))
return candidates[0][1]
def activateWindow(window, label):
if not window:
return
hwnd = getattr(window, "_hWnd", None)
try:
if window.isMinimized:
window.restore()
except Exception:
pass
if hwnd:
try:
ctypes.windll.user32.ShowWindow(hwnd, 9)
except Exception:
pass
try:
window.activate()
except Exception as e:
log("ERROR", f"{label} activate() failed detail={e}")
if hwnd:
try:
ctypes.windll.user32.BringWindowToTop(hwnd)
ctypes.windll.user32.SetForegroundWindow(hwnd)
except Exception as e:
log("ERROR", f"{label} SetForegroundWindow failed detail={e}")
time.sleep(0.15)
try:
pyautogui.click(window.left + window.width // 2, window.top + window.height // 2)
except Exception:
pass
time.sleep(0.1)
log("INFO", f"{label} window title={window.title}")
def setDiscordMute():
log("ACTION", "setDiscordMute called")
try:
activateDiscordWindow()
except Exception as e:
log("ERROR", f"Discord window unavailable detail={e}")
browser = findDiscordBrowserWindow() or findBrowserWindow()
activateWindow(browser, "Browser")
log("INFO", "Discord hotkey Ctrl+Shift+M")
pyautogui.hotkey("ctrl", "shift", "m")
activateWindow(findVrchatWindow(), "VRChat")
def setDiscordMuted(muted):
global _discord_muted
muted = bool(muted)
with _discord_mute_lock:
if _discord_muted == muted:
log("INFO", f"Discord mute already {'on' if muted else 'off'}")
return
setDiscordMute()
_discord_muted = muted