Public/Get-BATCRelayBotStatus.ps1

function Get-BATCRelayBotStatus {
    <#
    .SYNOPSIS
    Gets the current status of the BATC Relay Bot.
 
    .DESCRIPTION
    Checks whether the bot process is running, and returns detailed status information.
 
    .PARAMETER BotPath
    Path to the bot installation directory.
    Defaults to $env:LOCALAPPDATA\BATCRelayBot
 
    .PARAMETER PassThru
    Returns the status object. Without it the status is only printed, so a
    plain call does not dump the object's fields to the screen underneath the
    report it just wrote.
 
    .EXAMPLE
    Get-BATCRelayBotStatus
 
    .EXAMPLE
    $status = Get-BATCRelayBotStatus -PassThru
    if ($status.IsRunning) { "PID $($status.ProcessId)" }
 
    .OUTPUTS
    With -PassThru, a PSCustomObject with properties: IsRunning, ProcessId,
    PidFile, Uptime, OrphanedFromPidFile, LogFile, ErrorLogFile, ProcessInfo.
    #>


    param(
        [string]$BotPath = (Join-Path $env:LOCALAPPDATA "BATCRelayBot"),
        [switch]$PassThru
    )

    $pidFile = Join-Path $BotPath "bot.pid"
    $logFile = Join-Path $BotPath "logs\bot_output.log"
    $errorLogFile = Join-Path $BotPath "logs\bot_error.log"

    $status = [PSCustomObject]@{
        IsRunning    = $false
        ProcessId    = $null
        PidFile      = $pidFile
        Uptime       = $null
        OrphanedFromPidFile = $false
        LogFile      = $logFile
        ErrorLogFile = $errorLogFile
        ProcessInfo  = $null
    }

    # bot.pid is only a hint. Reporting "not running" purely because the file
    # is missing is how a live bot went unnoticed: it kept rejoining the voice
    # channel while both this command and Stop-BATCRelayBot insisted it was
    # stopped. The processes themselves are the fallback.
    $botPid = $null

    if (Test-Path $pidFile) {
        $recorded = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1)

        # Validate before use: Get-Process -Id throws a binding error on
        # anything non-numeric, so a truncated or garbled bot.pid used to take
        # the whole status command down instead of falling back.
        if ($recorded -match '^\s*\d+\s*$') {
            $candidate = [int]$recorded.Trim()
            if (Get-Process -Id $candidate -ErrorAction SilentlyContinue) {
                $botPid = $candidate
            }
        }
    }

    if (-not $botPid) {
        $found = @(Find-BotProcess -BotPath $BotPath)
        if ($found.Count -gt 0) {
            $botPid = $found[0]
            $status.OrphanedFromPidFile = $true
        }
    }

    if ($botPid) {
        $process = Get-Process -Id $botPid -ErrorAction SilentlyContinue
        if ($process) {
            $status.IsRunning = $true
            $status.ProcessId = $botPid
            $status.ProcessInfo = $process
            $status.Uptime = (Get-Date) - $process.StartTime
        }
    }

    if ($status.IsRunning) {
        Write-Host "Bot Status: RUNNING" -ForegroundColor Green
        Write-Host " PID: $($status.ProcessId)" -ForegroundColor Cyan
        Write-Host " Uptime: $($status.Uptime.ToString('hh\:mm\:ss'))" -ForegroundColor Cyan
        if ($status.OrphanedFromPidFile) {
            Write-Host " Note: found without a valid bot.pid - Stop-BATCRelayBot can still stop it." -ForegroundColor Yellow
        }
    } else {
        Write-Host "Bot Status: NOT RUNNING" -ForegroundColor Yellow
    }

    Write-Host " Output log: $($status.LogFile)" -ForegroundColor Cyan
    Write-Host " Error log: $($status.ErrorLogFile)" -ForegroundColor Cyan

    return (Out-CommandResult -Result $status -PassThru:$PassThru)
}