diff --git a/internal/app/discord.go b/internal/app/discord.go new file mode 100644 index 0000000..02dba8a --- /dev/null +++ b/internal/app/discord.go @@ -0,0 +1,122 @@ +package app + +import ( + "fmt" + "log" + "os/exec" +) + +func pressDiscordMuteHotkey() error { + script := ` +$ErrorActionPreference = "Stop" + +Add-Type @" +using System; +using System.Runtime.InteropServices; +public static class Win32 { + [StructLayout(LayoutKind.Sequential)] + public struct RECT { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + [DllImport("user32.dll")] + public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + public static extern bool SetCursorPos(int X, int Y); + [DllImport("user32.dll")] + public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo); +} +"@ + +function Click-WindowCenter($p) { + if ($null -eq $p) { return } + $hWnd = [IntPtr]$p.MainWindowHandle + if ($hWnd -eq [IntPtr]::Zero) { return } + + $rect = New-Object 'Win32+RECT' + if (-not [Win32]::GetWindowRect($hWnd, [ref]$rect)) { return } + + $x = [int](($rect.Left + $rect.Right) / 2) + $y = [int](($rect.Top + $rect.Bottom) / 2) + [Win32]::SetCursorPos($x, $y) | Out-Null + Start-Sleep -Milliseconds 50 + [Win32]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) + [Win32]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) + Start-Sleep -Milliseconds 100 +} + +function Get-DiscordWindows { + $windows = foreach ($p in Get-Process) { + $title = ($p.MainWindowTitle | Out-String).Trim() + if (-not $title) { continue } + if (-not $title.ToLower().Contains('discord')) { continue } + $p + } + if (-not $windows) { return @() } + return $windows +} + +function Get-BestDiscordWindow { + $windows = Get-DiscordWindows + if (-not $windows -or $windows.Count -eq 0) { return $null } + + $browserKeywords = @('google chrome', 'microsoft edge', 'mozilla firefox', 'brave', 'opera') + $ranked = $windows | Sort-Object { + $title = ($_.MainWindowTitle | Out-String).Trim().ToLower() + $score = 0 + foreach ($keyword in $browserKeywords) { + if ($title.Contains($keyword)) { $score += 10 } + } + if ($title -match '^\(\d+\)\s*discord') { $score += 20 } + if ($title.Contains('discord')) { $score += 5 } + $score + } -Descending + + return $ranked | Select-Object -First 1 +} + +function Get-VRChatWindow { + $windows = Get-Process | Where-Object { + $_.MainWindowTitle -and ( + $_.MainWindowTitle -eq 'VRChat' -or $_.MainWindowTitle.ToLower().StartsWith('vrchat ') + ) + } + if (-not $windows) { return $null } + return $windows | Sort-Object { $_.MainWindowHandle } -Descending | Select-Object -First 1 +} + +function Activate-Window($p) { + if ($null -eq $p) { return } + $wshell = New-Object -ComObject WScript.Shell + try { $null = $wshell.AppActivate($p.Id) } catch {} + Start-Sleep -Milliseconds 200 + Click-WindowCenter $p +} + +$discord = Get-BestDiscordWindow +if ($null -eq $discord) { exit 2 } + +Activate-Window $discord +$wshell = New-Object -ComObject WScript.Shell +Start-Sleep -Milliseconds 200 +$wshell.SendKeys('^+m') +Start-Sleep -Milliseconds 200 + +$vrchat = Get-VRChatWindow +if ($null -ne $vrchat) { + Activate-Window $vrchat +} +` + + cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script) + out, err := cmd.CombinedOutput() + if len(out) > 0 { + log.Printf("discord mute output: %s", string(out)) + } + if err != nil { + return fmt.Errorf("discord mute failed: %w", err) + } + return nil +}