440 lines
15 KiB
PowerShell
440 lines
15 KiB
PowerShell
# ==========================================
|
||
# 🎯 SING-BOX NATIVE INSTALLER FOR WINDOWS
|
||
# ==========================================
|
||
# Устанавливает нативный sing-box для поддержки UDP (Discord голосовые)
|
||
# Используется когда Docker запущен ЛОКАЛЬНО на Windows
|
||
# (Docker Desktop не поддерживает UDP)
|
||
# ==========================================
|
||
|
||
param(
|
||
[switch]$Force, # Принудительная переустановка
|
||
[string]$SubscriptionUrl = "" # URL подписки (для неинтерактивного режима)
|
||
)
|
||
|
||
# ==========================================
|
||
# КОНФИГУРАЦИЯ
|
||
# ==========================================
|
||
$SingboxVersion = "1.11.4"
|
||
$InstallDir = "C:\Tools\sing-box"
|
||
$LocalProxyPort = 1080
|
||
|
||
# Ссылки для скачивания
|
||
$SingboxUrl = "https://github.com/SagerNet/sing-box/releases/download/v$SingboxVersion/sing-box-$SingboxVersion-windows-amd64.zip"
|
||
|
||
# App Info (для заголовков подписки)
|
||
$AppName = "VPN-Proxy-Control by Dokril"
|
||
|
||
# ==========================================
|
||
# ЦВЕТА И ВЫВОД
|
||
# ==========================================
|
||
function Write-Step { param($msg) Write-Host "`n📦 $msg" -ForegroundColor Cyan }
|
||
function Write-Success { param($msg) Write-Host " ✅ $msg" -ForegroundColor Green }
|
||
function Write-Warning { param($msg) Write-Host " ⚠️ $msg" -ForegroundColor Yellow }
|
||
function Write-Error { param($msg) Write-Host " ❌ $msg" -ForegroundColor Red }
|
||
function Write-Info { param($msg) Write-Host " ℹ️ $msg" -ForegroundColor Gray }
|
||
|
||
# ==========================================
|
||
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
||
# ==========================================
|
||
|
||
function Get-HWID {
|
||
$hwidFile = "$InstallDir\hwid"
|
||
if (Test-Path $hwidFile) {
|
||
return (Get-Content $hwidFile -Raw).Trim()
|
||
}
|
||
|
||
$hwid = [System.Guid]::NewGuid().ToString("N").Substring(0, 16)
|
||
|
||
if (!(Test-Path $InstallDir)) {
|
||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||
}
|
||
|
||
Set-Content -Path $hwidFile -Value $hwid
|
||
return $hwid
|
||
}
|
||
|
||
function Get-SystemInfo {
|
||
return @{
|
||
os = "windows"
|
||
version = [System.Environment]::OSVersion.Version.Major.ToString()
|
||
}
|
||
}
|
||
|
||
function Get-SubscriptionData {
|
||
param([string]$Url)
|
||
|
||
Write-Info "Загружаю подписку..."
|
||
|
||
$hwid = Get-HWID
|
||
$sysInfo = Get-SystemInfo
|
||
|
||
$headers = @{
|
||
"User-Agent" = "singbox"
|
||
"x-hwid" = $hwid
|
||
"x-device-os" = $sysInfo.os
|
||
"x-ver-os" = $sysInfo.version
|
||
"x-device-model" = $AppName
|
||
}
|
||
|
||
try {
|
||
$response = Invoke-WebRequest -Uri $Url -Headers $headers -TimeoutSec 15 -UseBasicParsing
|
||
$config = $response.Content | ConvertFrom-Json
|
||
|
||
$userInfo = @{}
|
||
$userInfoHeader = $response.Headers["subscription-userinfo"]
|
||
if ($userInfoHeader) {
|
||
$parts = $userInfoHeader -split ";"
|
||
foreach ($part in $parts) {
|
||
if ($part -match "(\w+)=(\d+)") {
|
||
$userInfo[$matches[1]] = [int64]$matches[2]
|
||
}
|
||
}
|
||
}
|
||
|
||
return @{
|
||
success = $true
|
||
config = $config
|
||
userInfo = $userInfo
|
||
}
|
||
}
|
||
catch {
|
||
return @{
|
||
success = $false
|
||
error = $_.Exception.Message
|
||
}
|
||
}
|
||
}
|
||
|
||
function Select-Server {
|
||
param($Config)
|
||
|
||
$outbounds = $Config.outbounds
|
||
$servers = @()
|
||
|
||
foreach ($outbound in $outbounds) {
|
||
if ($outbound.type -in @("vless", "vmess", "trojan", "shadowsocks", "hysteria2")) {
|
||
$servers += @{
|
||
tag = $outbound.tag
|
||
type = $outbound.type
|
||
server = $outbound.server
|
||
server_port = $outbound.server_port
|
||
outbound = $outbound
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($servers.Count -eq 0) {
|
||
Write-Error "Серверы не найдены в подписке!"
|
||
return $null
|
||
}
|
||
|
||
Write-Host "`n🌐 Доступные серверы:" -ForegroundColor Yellow
|
||
for ($i = 0; $i -lt $servers.Count; $i++) {
|
||
$s = $servers[$i]
|
||
Write-Host " [$($i+1)] $($s.tag) ($($s.type)) - $($s.server):$($s.server_port)"
|
||
}
|
||
|
||
$choice = Read-Host "`n👉 Выберите сервер (1-$($servers.Count))"
|
||
$index = [int]$choice - 1
|
||
|
||
if ($index -lt 0 -or $index -ge $servers.Count) {
|
||
Write-Error "Неверный выбор!"
|
||
return $null
|
||
}
|
||
|
||
return $servers[$index]
|
||
}
|
||
|
||
function New-SingboxConfig {
|
||
param($Outbound, $Port)
|
||
|
||
$config = @{
|
||
log = @{
|
||
level = "info"
|
||
timestamp = $true
|
||
}
|
||
dns = @{
|
||
independent_cache = $true
|
||
}
|
||
inbounds = @(
|
||
@{
|
||
type = "socks"
|
||
tag = "socks-in"
|
||
listen = "127.0.0.1"
|
||
listen_port = $Port
|
||
}
|
||
)
|
||
outbounds = @(
|
||
$Outbound,
|
||
@{
|
||
type = "direct"
|
||
tag = "direct"
|
||
}
|
||
)
|
||
route = @{
|
||
final = $Outbound.tag
|
||
auto_detect_interface = $true
|
||
}
|
||
}
|
||
|
||
return $config
|
||
}
|
||
|
||
function Install-SingboxTask {
|
||
Write-Step "Установка sing-box через Task Scheduler..."
|
||
|
||
$exePath = "$InstallDir\sing-box.exe"
|
||
$configPath = "$InstallDir\config.json"
|
||
$taskName = "SingBoxProxy"
|
||
|
||
# Удаляем старую задачу
|
||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||
|
||
# Создаём новую
|
||
$action = New-ScheduledTaskAction -Execute "$exePath" -Argument "run -c `"$configPath`"" -WorkingDirectory $InstallDir
|
||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
|
||
|
||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
|
||
|
||
# Запускаем сразу
|
||
Start-ScheduledTask -TaskName $taskName
|
||
|
||
Write-Success "Задача создана и запущена!"
|
||
return $true
|
||
}
|
||
|
||
function Stop-SingboxTask {
|
||
Stop-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
||
Stop-Process -Name "sing-box" -Force -ErrorAction SilentlyContinue
|
||
}
|
||
|
||
# ==========================================
|
||
# ПРОВЕРКА ПРАВ АДМИНИСТРАТОРА
|
||
# ==========================================
|
||
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||
Write-Host "⛔ Запустите скрипт от имени АДМИНИСТРАТОРА!" -ForegroundColor Red
|
||
Start-Sleep -Seconds 3
|
||
Exit 1
|
||
}
|
||
|
||
Clear-Host
|
||
Write-Host "==========================================" -ForegroundColor Cyan
|
||
Write-Host " 🎯 SING-BOX NATIVE INSTALLER " -ForegroundColor Cyan
|
||
Write-Host " для Discord UDP поддержки " -ForegroundColor Cyan
|
||
Write-Host "==========================================" -ForegroundColor Cyan
|
||
Write-Host ""
|
||
Write-Host "Этот скрипт нужен когда Docker запущен" -ForegroundColor Gray
|
||
Write-Host "ЛОКАЛЬНО на Windows (Docker Desktop)." -ForegroundColor Gray
|
||
Write-Host "Docker Desktop не поддерживает UDP." -ForegroundColor Gray
|
||
|
||
# ==========================================
|
||
# ПРОВЕРКА СУЩЕСТВУЮЩЕЙ УСТАНОВКИ
|
||
# ==========================================
|
||
$singboxExists = Test-Path "$InstallDir\sing-box.exe"
|
||
$taskExists = Get-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
||
|
||
if ($singboxExists -and $taskExists -and -not $Force) {
|
||
Write-Host ""
|
||
Write-Host "==========================================" -ForegroundColor Green
|
||
Write-Host " ✅ SING-BOX УЖЕ УСТАНОВЛЕН! " -ForegroundColor Green
|
||
Write-Host "==========================================" -ForegroundColor Green
|
||
Write-Host ""
|
||
|
||
$taskInfo = Get-ScheduledTask -TaskName "SingBoxProxy"
|
||
$taskState = $taskInfo.State
|
||
|
||
Write-Host "📊 Статус: " -NoNewline -ForegroundColor Yellow
|
||
if ($taskState -eq "Running") {
|
||
Write-Host "РАБОТАЕТ" -ForegroundColor Green
|
||
}
|
||
else {
|
||
Write-Host "$taskState" -ForegroundColor Red
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host "📁 Расположение: $InstallDir" -ForegroundColor Yellow
|
||
Write-Host "🔌 Локальный SOCKS5: 127.0.0.1:$LocalProxyPort" -ForegroundColor Yellow
|
||
Write-Host ""
|
||
|
||
Write-Host "Что вы хотите сделать?" -ForegroundColor Yellow
|
||
Write-Host " [1] Запустить" -ForegroundColor White
|
||
Write-Host " [2] Остановить" -ForegroundColor White
|
||
Write-Host " [3] Показать конфиг" -ForegroundColor White
|
||
Write-Host " [4] Переустановить" -ForegroundColor White
|
||
Write-Host " [q] Выход" -ForegroundColor White
|
||
Write-Host ""
|
||
|
||
$choice = Read-Host "👉 Выбор (1-4/q)"
|
||
|
||
switch ($choice) {
|
||
"1" {
|
||
Start-ScheduledTask -TaskName "SingBoxProxy"
|
||
Start-Sleep -Seconds 2
|
||
Write-Success "Запущено!"
|
||
exit 0
|
||
}
|
||
"2" {
|
||
Stop-SingboxTask
|
||
Write-Success "Остановлено!"
|
||
exit 0
|
||
}
|
||
"3" {
|
||
Write-Host "`n📄 Конфигурация:" -ForegroundColor Cyan
|
||
Get-Content "$InstallDir\config.json" | ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||
exit 0
|
||
}
|
||
"4" {
|
||
Write-Info "Переустановка..."
|
||
}
|
||
default {
|
||
exit 0
|
||
}
|
||
}
|
||
}
|
||
|
||
# ==========================================
|
||
# УСТАНОВКА
|
||
# ==========================================
|
||
|
||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||
|
||
# ШАГ 1: Скачиваем sing-box
|
||
Write-Step "Скачиваю sing-box v$SingboxVersion..."
|
||
|
||
if (!(Test-Path $InstallDir)) {
|
||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||
}
|
||
|
||
$zipFile = "$env:TEMP\sing-box.zip"
|
||
|
||
try {
|
||
Invoke-WebRequest -Uri $SingboxUrl -OutFile $zipFile -UseBasicParsing
|
||
Write-Success "Скачано!"
|
||
}
|
||
catch {
|
||
Write-Error "Ошибка скачивания: $_"
|
||
exit 1
|
||
}
|
||
|
||
Write-Info "Распаковываю..."
|
||
Expand-Archive -Path $zipFile -DestinationPath $env:TEMP -Force
|
||
|
||
$extractedDir = Get-ChildItem "$env:TEMP\sing-box-*" -Directory | Select-Object -First 1
|
||
Copy-Item "$($extractedDir.FullName)\sing-box.exe" "$InstallDir\sing-box.exe" -Force
|
||
|
||
Remove-Item $zipFile -Force -ErrorAction SilentlyContinue
|
||
Remove-Item $extractedDir.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
||
|
||
Write-Success "sing-box установлен в $InstallDir"
|
||
|
||
# ШАГ 2: Получаем подписку
|
||
Write-Step "Настройка подписки..."
|
||
|
||
$savedSubFile = Join-Path $PSScriptRoot "data\subscription.json"
|
||
$subUrl = $SubscriptionUrl
|
||
|
||
if ([string]::IsNullOrWhiteSpace($subUrl) -and (Test-Path $savedSubFile)) {
|
||
try {
|
||
$savedSub = Get-Content $savedSubFile -Raw | ConvertFrom-Json
|
||
if ($savedSub.url) {
|
||
Write-Info "Найдена сохранённая подписка"
|
||
Write-Host " URL: $($savedSub.url)" -ForegroundColor Gray
|
||
$useSaved = Read-Host " Использовать? (y/n)"
|
||
if ($useSaved -eq "y") {
|
||
$subUrl = $savedSub.url
|
||
}
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
|
||
if ([string]::IsNullOrWhiteSpace($subUrl)) {
|
||
Write-Host "`n🔗 Введите URL подписки:" -ForegroundColor Yellow
|
||
$subUrl = Read-Host "👉"
|
||
}
|
||
|
||
if ([string]::IsNullOrWhiteSpace($subUrl)) {
|
||
Write-Error "URL подписки не указан!"
|
||
exit 1
|
||
}
|
||
|
||
$subResult = Get-SubscriptionData -Url $subUrl
|
||
|
||
if (-not $subResult.success) {
|
||
Write-Error "Ошибка загрузки подписки: $($subResult.error)"
|
||
exit 1
|
||
}
|
||
|
||
Write-Success "Подписка загружена!"
|
||
|
||
if ($subResult.userInfo.Count -gt 0) {
|
||
$ui = $subResult.userInfo
|
||
if ($ui.upload -and $ui.download -and $ui.total) {
|
||
$usedGB = [math]::Round(($ui.upload + $ui.download) / 1GB, 2)
|
||
$totalGB = [math]::Round($ui.total / 1GB, 2)
|
||
Write-Info "Трафик: $usedGB / $totalGB GB"
|
||
}
|
||
}
|
||
|
||
# ШАГ 3: Выбираем сервер
|
||
$selectedServer = Select-Server -Config $subResult.config
|
||
|
||
if (-not $selectedServer) {
|
||
exit 1
|
||
}
|
||
|
||
Write-Success "Выбран сервер: $($selectedServer.tag)"
|
||
|
||
# ШАГ 4: Генерируем конфиг
|
||
Write-Step "Генерирую конфигурацию..."
|
||
|
||
$config = New-SingboxConfig -Outbound $selectedServer.outbound -Port $LocalProxyPort
|
||
$configJson = $config | ConvertTo-Json -Depth 10
|
||
|
||
Set-Content -Path "$InstallDir\config.json" -Value $configJson -Encoding UTF8
|
||
Write-Success "Конфиг сохранён: $InstallDir\config.json"
|
||
|
||
# ШАГ 5: Запускаем
|
||
Stop-SingboxTask
|
||
Start-Sleep -Seconds 1
|
||
|
||
$installed = Install-SingboxTask
|
||
|
||
if (-not $installed) {
|
||
Write-Error "Не удалось установить задачу!"
|
||
exit 1
|
||
}
|
||
|
||
Start-Sleep -Seconds 3
|
||
|
||
# Проверяем порт
|
||
Write-Info "Проверяю доступность порта..."
|
||
$portCheck = Test-NetConnection -ComputerName 127.0.0.1 -Port $LocalProxyPort -WarningAction SilentlyContinue
|
||
|
||
if ($portCheck.TcpTestSucceeded) {
|
||
Write-Success "Порт $LocalProxyPort доступен!"
|
||
}
|
||
else {
|
||
Write-Warning "Порт $LocalProxyPort пока недоступен. Подождите несколько секунд."
|
||
}
|
||
|
||
# Финал
|
||
Write-Host ""
|
||
Write-Host "==========================================" -ForegroundColor Green
|
||
Write-Host " 🎉 SING-BOX УСТАНОВЛЕН! " -ForegroundColor Green
|
||
Write-Host "==========================================" -ForegroundColor Green
|
||
Write-Host ""
|
||
Write-Host "📡 Локальный SOCKS5: 127.0.0.1:$LocalProxyPort" -ForegroundColor Yellow
|
||
Write-Host " Сервер: $($selectedServer.tag)" -ForegroundColor White
|
||
Write-Host ""
|
||
Write-Host "Теперь запустите discord-windows-hack.ps1" -ForegroundColor Cyan
|
||
Write-Host "и укажите прокси: 127.0.0.1:$LocalProxyPort" -ForegroundColor Cyan
|
||
Write-Host ""
|
||
Write-Host "📋 Полезные команды:" -ForegroundColor Gray
|
||
Write-Host " Статус: Get-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
||
Write-Host " Запустить: Start-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
||
Write-Host " Остановить: Stop-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
||
Write-Host ""
|