Public/Stop-BATCRelayBot.ps1
|
function Stop-BATCRelayBot { <# .SYNOPSIS Cleanly stops the BATC Relay Bot. .DESCRIPTION Signals the bot to leave the voice channel gracefully and exit. Falls back to force-kill if graceful shutdown times out. .PARAMETER BotPath Path to the bot installation directory. Defaults to $env:LOCALAPPDATA\BATCRelayBot .PARAMETER Timeout Seconds to wait for graceful shutdown before force-killing (default: 15). .EXAMPLE Stop-BATCRelayBot .EXAMPLE Stop-BATCRelayBot -BotPath "D:\MyBot\BATCRelayBot" -Timeout 20 #> param( [string]$BotPath = (Join-Path $env:LOCALAPPDATA "BATCRelayBot"), [int]$Timeout = 15 ) $pidFile = Join-Path $BotPath "bot.pid" # bot.pid is a hint, not the authority. Trusting it exclusively is how a # running bot got orphaned: a stale PID made this function delete the file # and report "not running", after which every later call said the same # while the real process kept rejoining the voice channel with no way left # to stop it. Stop-BotProcess always looks at the actual processes. $running = @(Find-BotProcess -BotPath $BotPath) if ($running.Count -eq 0) { # No bot, but a watcher: the bot ended and the watcher is in its # restart delay. Before this, "No running bot found" here was # followed ten seconds later by a bot nobody had asked for. The # signal is what the watcher waits for during that delay. $watchers = @(Find-BotWatcher -BotPath $BotPath) if ($watchers.Count -gt 0) { Write-Host "The bot is not running, but its watcher (PID $($watchers -join ', ')) is about to restart it - telling it not to..." -ForegroundColor Cyan $stopSignal = Join-Path $BotPath "stop.signal" New-Item -Path $stopSignal -ItemType File -Force -ErrorAction SilentlyContinue | Out-Null $deadline = (Get-Date).AddSeconds($Timeout) while ((Get-Date) -lt $deadline -and @(Find-BotWatcher -BotPath $BotPath).Count -gt 0) { Start-Sleep -Milliseconds 500 } if (@(Find-BotWatcher -BotPath $BotPath).Count -gt 0) { # A bot may have come up in the meantime; it reads the same # signal. The watcher itself is ended so nothing follows. Stop-BotWatcher -BotPath $BotPath | Out-Null Stop-BotProcess -BotPath $BotPath -TimeoutSeconds $Timeout | Out-Null Write-Host "The watcher did not stop on its own and was ended." -ForegroundColor Yellow } else { Write-Host "The watcher has stopped; the bot will not be restarted." -ForegroundColor Green } Remove-Item $stopSignal -Force -ErrorAction SilentlyContinue Remove-Item $pidFile -Force -ErrorAction SilentlyContinue return } Write-Host "No running bot found for $BotPath." -ForegroundColor Yellow Remove-Item $pidFile -Force -ErrorAction SilentlyContinue return } Write-Host "Stopping the bot (PID $($running -join ', ')) - waiting for it to leave the channel..." -ForegroundColor Cyan $result = Stop-BotProcess -BotPath $BotPath -TimeoutSeconds $Timeout if (-not $result.Stopped) { Write-Host "The bot could not be stopped. Try again, or end the process manually." -ForegroundColor Red return } if ($result.Method -eq 'graceful') { Write-Host "Bot shut down cleanly." -ForegroundColor Green } else { Write-Host "Bot did not respond in time and was terminated." -ForegroundColor Yellow # The watcher records the exit as -1, which is also what Task Manager # looks like. This line is what tells the two apart afterwards. Write-InstallLog -LogPath (Join-Path $BotPath "install.log") -Level WARN ` -Message "Bot (PID $($running -join ', ')) did not respond to stop.signal within ${Timeout}s and was terminated by Stop-BATCRelayBot" # A terminated bot takes ffmpeg with it without closing the capture # it held on a VoiceMeeter bus, and VoiceMeeter's engine can be left # in a state where nothing on the machine plays audio. Restarting the # VoiceMeeter process does not clear it - the engine has to be # restarted, or VoiceMeeter shut down from its own tray menu. Write-Host "" Write-Host " If audio stops working across the machine, restart VoiceMeeter's" -ForegroundColor Yellow Write-Host " audio engine: right-click its tray icon > Restart Audio Engine." -ForegroundColor Yellow Write-Host " Killing and relaunching the VoiceMeeter process does not fix it." -ForegroundColor Yellow } Remove-Item $pidFile -Force -ErrorAction SilentlyContinue } |