Public/Install-BATCRelayBot.ps1

function Install-BATCRelayBot {
    <#
    .SYNOPSIS
    Installs all prerequisites and generates config.json for the BATC Relay Bot.
 
    .DESCRIPTION
    Installs Python and ffmpeg (via winget), pip dependencies, detects VoiceMeeter
    device, and interactively prompts for Discord configuration.
    Installs for the current user only - no admin rights required.
 
    .PARAMETER ProjectPath
    Path to the project directory containing bot.py and requirements.txt.
    Defaults to the current directory.
 
    .EXAMPLE
    Install-BATCRelayBot -ProjectPath "C:\MyProjects\ATC-Relay-Bot"
    #>


    param(
        [string]$ProjectPath = (Get-Location).Path
    )

    $ErrorActionPreference = "Stop"
    Set-Location $ProjectPath

    # Check winget
    Write-Step "Checking prerequisites"
    if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
        Write-Host "ERROR: winget (Windows Package Manager) not found." -ForegroundColor Red
        Write-Host "Install 'App Installer' from the Microsoft Store:" -ForegroundColor Red
        Write-Host "https://apps.microsoft.com/detail/9nblggh4nns1" -ForegroundColor Red
        exit 1
    }
    Write-Host "winget found." -ForegroundColor Green

    # Install Python
    Write-Step "Python"
    $pythonExe = Find-PerUserPython
    if ($pythonExe) {
        Write-Host "Python already installed: $pythonExe" -ForegroundColor Green
    } else {
        Write-Host "Python not found, installing latest via winget..." -ForegroundColor Cyan
        $searchOutput = winget search "Python.Python.3." --source winget --accept-source-agreements 2>$null
        $matches = [regex]::Matches(($searchOutput -join "`n"), 'Python\.Python\.3\.(\d+)')
        $latestId = $null
        $latestMinor = -1
        foreach ($m in $matches) {
            $minor = [int]$m.Groups[1].Value
            if ($minor -gt $latestMinor) {
                $latestMinor = $minor
                $latestId = $m.Value
            }
        }
        if (-not $latestId) {
            Write-Host "ERROR: Could not find Python via winget. Install manually from python.org" -ForegroundColor Red
            exit 1
        }
        Write-Host "Installing $latestId (current user only)..." -ForegroundColor Cyan
        winget install --id $latestId -e --scope user --silent --accept-package-agreements --accept-source-agreements
        if ($LASTEXITCODE -ne 0) {
            Write-Host "ERROR: Python installation failed (exit code $LASTEXITCODE)." -ForegroundColor Red
            exit 1
        }
        $pythonExe = Find-PerUserPython
        if (-not $pythonExe) {
            Write-Host "ERROR: Python was installed but could not be located. Restart the terminal and try again." -ForegroundColor Red
            exit 1
        }
        Write-Host "Python installed: $pythonExe" -ForegroundColor Green
    }

    # Install ffmpeg
    Write-Step "ffmpeg"
    $ffmpegExe = Find-FfmpegExe
    if ($ffmpegExe) {
        Write-Host "ffmpeg already installed: $ffmpegExe" -ForegroundColor Green
    } else {
        Write-Host "ffmpeg not found, installing via winget..." -ForegroundColor Cyan
        winget install --id Gyan.FFmpeg -e --scope user --silent --accept-package-agreements --accept-source-agreements
        if ($LASTEXITCODE -ne 0) {
            Write-Host "ERROR: ffmpeg installation failed (exit code $LASTEXITCODE)." -ForegroundColor Red
            exit 1
        }
        $ffmpegExe = Find-FfmpegExe
        if (-not $ffmpegExe) {
            Write-Host "ERROR: ffmpeg was installed but could not be located." -ForegroundColor Red
            exit 1
        }
        Write-Host "ffmpeg installed: $ffmpegExe" -ForegroundColor Green
    }

    $ffmpegDir = Split-Path -Parent $ffmpegExe
    $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
    if ($userPath -notlike "*$ffmpegDir*") {
        Write-Host "Adding ffmpeg to user PATH: $ffmpegDir" -ForegroundColor Cyan
        $newUserPath = if ($userPath) { "$userPath;$ffmpegDir" } else { $ffmpegDir }
        [Environment]::SetEnvironmentVariable("Path", $newUserPath, "User")
    } else {
        Write-Host "ffmpeg is already on PATH." -ForegroundColor Green
    }

    if ($env:Path -notlike "*$ffmpegDir*") {
        $env:Path = "$env:Path;$ffmpegDir"
    }

    # Install VoiceMeeter
    Write-Step "VoiceMeeter"
    $voicemeeterInstalled = Get-ChildItem "C:\Program Files (x86)\VB\Voicemeeter\" -ErrorAction SilentlyContinue
    if ($voicemeeterInstalled) {
        Write-Host "VoiceMeeter already installed" -ForegroundColor Green
    } else {
        Write-Host "VoiceMeeter not found, installing via winget..." -ForegroundColor Cyan
        winget install --id VB-Audio.VoiceMeeter -e --scope user --silent --accept-package-agreements --accept-source-agreements
        if ($LASTEXITCODE -ne 0) {
            Write-Host "WARNING: VoiceMeeter installation may have failed (exit code $LASTEXITCODE)." -ForegroundColor Yellow
            Write-Host "VoiceMeeter requires restart. Please visit https://vb-audio.com/Voicemeeter/ to install manually if needed." -ForegroundColor Yellow
        } else {
            Write-Host "VoiceMeeter installed" -ForegroundColor Green
            Write-Host "NOTE: VoiceMeeter requires system restart to fully activate." -ForegroundColor Cyan
        }
    }

    # Install pip requirements
    Write-Step "Python dependencies"
    & $pythonExe -m pip install --upgrade pip --quiet
    & $pythonExe -m pip install -r (Join-Path $ProjectPath "requirements.txt")
    if ($LASTEXITCODE -ne 0) {
        Write-Host "ERROR: pip install failed." -ForegroundColor Red
        exit 1
    }
    Write-Host "Python dependencies installed." -ForegroundColor Green

    # Detect VoiceMeeter
    Write-Step "Detecting VoiceMeeter device"
    $tempOut = New-TemporaryFile
    $tempErr = New-TemporaryFile
    Start-Process -FilePath $ffmpegExe -ArgumentList @("-list_devices", "true", "-f", "dshow", "-i", "dummy") `
        -RedirectStandardOutput $tempOut -RedirectStandardError $tempErr -NoNewWindow -Wait
    $listOutput = (Get-Content $tempOut -Raw -ErrorAction SilentlyContinue) + "`n" + (Get-Content $tempErr -Raw -ErrorAction SilentlyContinue)
    Remove-Item $tempOut, $tempErr -ErrorAction SilentlyContinue

    $deviceMatches = [regex]::Matches($listOutput, '"(Voicemeeter Out [^"]+)"\s*\(audio\)')
    $devices = $deviceMatches | ForEach-Object { $_.Groups[1].Value } | Select-Object -Unique

    if ($devices.Count -eq 0) {
        Write-Host "No VoiceMeeter device found. Is VoiceMeeter installed and started?" -ForegroundColor Yellow
        $audioDeviceName = Prompt-WithDefault -Message "Enter device name manually" -Default $null
    } elseif ($devices.Count -eq 1) {
        $audioDeviceName = $devices[0]
        Write-Host "Found: $audioDeviceName" -ForegroundColor Green
    } else {
        Write-Host "Multiple VoiceMeeter devices found:" -ForegroundColor Cyan
        for ($i = 0; $i -lt $devices.Count; $i++) {
            Write-Host " [$i] $($devices[$i])"
        }
        $choice = -1
        while ($choice -lt 0 -or $choice -ge $devices.Count) {
            $raw = Read-Host "Which device? (enter number)"
            [int]::TryParse($raw, [ref]$choice) | Out-Null
        }
        $audioDeviceName = $devices[$choice]
    }

    # Ask for Discord config
    Write-Step "Discord bot configuration"
    $configPath = Join-Path $ProjectPath "config.json"
    $writeConfig = $true
    if (Test-Path $configPath) {
        $answer = Read-Host "config.json exists. Overwrite? (y/N)"
        if ($answer -notmatch '^[yY]') {
            $writeConfig = $false
            Write-Host "config.json will not be changed." -ForegroundColor Yellow
        }
    }

    if ($writeConfig) {
        Write-Host "Find your bot token at: https://discord.com/developers/applications" -ForegroundColor Cyan
        $secureToken = Read-Host "Bot token (input will not be shown)" -AsSecureString
        $botToken = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
            [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureToken)
        )

        Write-Host "Get IDs by enabling Developer Mode in Discord settings, then right-click." -ForegroundColor Cyan
        $guildId = Prompt-WithDefault -Message "Server ID (guild_id)" -Default $null
        $voiceChannelId = Prompt-WithDefault -Message "Voice channel ID" -Default $null

        $defaultVoicemeeterPath = "C:\Program Files (x86)\VB\Voicemeeter\voicemeeter_x64.exe"
        if (-not (Test-Path $defaultVoicemeeterPath)) { $defaultVoicemeeterPath = $null }
        $voicemeeterPath = Prompt-FilePath -Message "Path to VoiceMeeter (voicemeeter_x64.exe)" -Filter "Programs (*.exe)|*.exe"
        if ([string]::IsNullOrWhiteSpace($voicemeeterPath) -and $defaultVoicemeeterPath) {
            $voicemeeterPath = $defaultVoicemeeterPath
        }
        $voicemeeterProcessName = [System.IO.Path]::GetFileNameWithoutExtension($voicemeeterPath)

        $batcPath = Prompt-FilePath -Message "Path to BeyondATC.exe" -Filter "Programs (*.exe)|*.exe"
        $batcProcessName = [System.IO.Path]::GetFileNameWithoutExtension($batcPath)

        $voicemeeterWait = Prompt-WithDefault -Message "Wait time after starting VoiceMeeter (seconds)" -Default "6"
        $batcWait = Prompt-WithDefault -Message "Wait time after starting BeyondATC (seconds)" -Default "8"

        $configObject = [ordered]@{
            bot_token                = $botToken
            guild_id                 = [int64]$guildId
            voice_channel_id         = [int64]$voiceChannelId
            audio_device_name        = $audioDeviceName
            python_path              = $pythonExe
            voicemeeter_path         = $voicemeeterPath
            voicemeeter_process_name = $voicemeeterProcessName
            voicemeeter_wait_seconds = [int]$voicemeeterWait
            batc_path                = $batcPath
            batc_process_name        = $batcProcessName
            batc_wait_seconds        = [int]$batcWait
        }

        $jsonContent = $configObject | ConvertTo-Json -Depth 5
        [System.IO.File]::WriteAllText($configPath, $jsonContent, (New-Object System.Text.UTF8Encoding($false)))
        Write-Host "config.json created." -ForegroundColor Green
    }

    Write-Step "Setup complete"
    Write-Host "Technical prerequisites are installed and configured." -ForegroundColor Green
    Write-Host ""
    Write-Host "Still to do (manual):" -ForegroundColor Cyan
    Write-Host " 1. Create a Discord bot at https://discord.com/developers/applications" -ForegroundColor Cyan
    Write-Host " 2. Invite it to your server with 'Connect' and 'Speak' permissions" -ForegroundColor Cyan
    Write-Host " 3. Set channel permissions for the bot's role: View Channel, Connect, Speak" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Then start the bot with: Start-BATCRelayBot" -ForegroundColor Green
}