Initial VRC OSC commit
This commit is contained in:
203
src/discord_control/actions.py
Normal file
203
src/discord_control/actions.py
Normal 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
|
||||
Reference in New Issue
Block a user