# Модуль: gper.psm1 # Версия: 2.9.0 # Дата: 2026-06-04 # Описание: GPER PowerShell Module #Requires -Version 5.0 # ================================================================ # КОНСТАНТЫ МОДУЛЯ # ================================================================ $Script:GPER_VERSION = '2.8.0' $Script:GPER_DATE = '2026-06-04' $Script:GPER_URL_PS = 'https://ps.gper.ru' $Script:GPER_LOCAL_DIR = Join-Path $env:APPDATA 'gper.ru' $Script:GPER_LOCAL_PSM1 = Join-Path $Script:GPER_LOCAL_DIR 'gper.psm1' # Цвет по умолчанию для Write-Host (если цвет не указан) $Script:GPER_FG_DEFAULT = 'Cyan' $Script:GPER_BG_DEFAULT = $null # $null = не задаётся BackgroundColor # ================================================================ # КАРТА ЦВЕТОВ ДЛЯ Show-Message # Версия: 1.0.0 | Дата: 2026-03-31 # # Структура: [string]цвет → [string[]]ключевые слова (нижний регистр) # Приоритет совпадений: Red > Yellow > Green > White (fallback) # ================================================================ $Script:GPER_COLOR_MAP = [ordered]@{ Red = @( 'ошибка', 'ошибку', 'ошибки', 'error', 'fail', 'failed', 'критично', 'критическая', 'critical', 'fatal', 'не удалось', 'недоступен', 'недоступна', 'отказ' ) Yellow = @( 'внимание', 'предупреждение', 'предупреждение!', 'warning', 'warn', 'устарел', 'устарела', 'устаревший', 'deprecated', 'внимание!' ) Green = @( 'успех', 'успешно', 'успешная', 'готово', 'выполнено', 'завершено', 'завершена', 'установлен', 'установлено', 'ok', 'success', 'done', 'complete', 'completed', 'создан', 'создана', 'скопировано', 'загружено' ) } # ================================================================ # Модули-зависимости # Версия: 1.0.0 | Дата: 2026-04-27 # # Здесь можно импортировать другие модули, если нужно. # ================================================================ $Script:GPER_REQUIREMENTS = @( @{NAME = 'PSReadLine'; MIN = [Version]'2.0.0' }, @{NAME = 'Posh-Git'; MIN = [Version]'1.1.0' }, @{NAME = 'Microsoft.PowerShell.Utility'; MIN = [Version]'1.0.0' }, @{NAME = 'MrToolkit'; MIN = [Version]'1.0.0' } @{NAME = '7Zip4Powershell'; MIN = [Version]'2.10.0' } ) function Install-GPER_REQUIREMENTS { try { foreach ($req in $Script:GPER_REQUIREMENTS) { $name = $req.NAME $min = $req.MIN if (-not (Get-Module -ListAvailable -Name $name )) { Write-Host "[!] Устанавливаю модуль '$name' версии $min" -ForegroundColor Yellow Install-Module -Name $name -Force -Scope CurrentUser Import-Module -Name $name -Force -Global } } } catch { Write-Host "[X] Не удалось установить зависимости: $_" -ForegroundColor Red } try { foreach ($req in $Script:GPER_REQUIREMENTS) { $name = $req.NAME $min = $req.MIN if (-not (Get-Module -ListAvailable -Name $name )) { Write-Host "[!] Устанавливаю модуль '$name' версии $min" -ForegroundColor Yellow Install-Module -Name $name -Force -Scope CurrentUser Import-Module -Name $name -Force -Global } } } catch { Write-Host "[X] Не удалось установить зависимости: $_" -ForegroundColor Red } } function Get-Latest7zVersion { param([string]$BaseUrl) try { if ([Environment]::Is64BitOperatingSystem) { $Architecture = 'x64' } else { $Architecture = 'x86' } Write-Host "Поиск последней версии 7z для $Architecture..." -ForegroundColor Cyan $BaseUrl = 'https://gper.ru/soft/arc/7z' $BaseUrl = $BaseUrl.TrimEnd('/') + '/' # Убедимся, что URL заканчивается на '/' Write-Host "Получаем список файлов с сервера $BaseUrl" -ForegroundColor Cyan $response = Invoke-WebRequest -Uri $BaseUrl -UseBasicParsing -ErrorAction Stop $response # Read-Host '105' # {7z2601-x64.exe, 7z2601-x86.exe, ...} Write-Host 'Ищем установочные файлы для нужной архитектуры' -ForegroundColor Cyan $pattern = if ($Architecture -eq 'x64') { '7z\d{4}-x64\.exe' } else { '7z\d{4}-x86\.exe' } # Read-Host " 110 pattern = $pattern" $files = $response.Links | Where-Object { $_.href -match $pattern } # Read-Host " 110 files = $files" if (!$files) { Write-Host 'Не найдены установочные файлы 7z' -ForegroundColor Red return $null } Write-Host 'Фильтруем по архитектуре' -ForegroundColor Cyan $archFiles = $files | Where-Object { $_.href -match $Architecture } if (!$archFiles) { Write-Host "Не найдены файлы для архитектуры $Architecture" -ForegroundColor Red return $null } Write-Host 'Берем первый (предполагается, что сервер отдает в правильном порядке)' -ForegroundColor Cyan $latestFile = $BaseUrl + $archFiles[0].href # Read-Host " 130 latestFile = $latestFile" Write-Host 'Формируем полный URL' -ForegroundColor Cyan if ($latestFile -notmatch '^https?://') { $latestFile = $BaseUrl.TrimEnd('/') + '/' + $latestFile.TrimStart('./') } Write-Host "Найдена версия: $latestFile" -ForegroundColor Green return $latestFile } catch { Write-Host "Ошибка при получении списка версий: $($_.Exception.Message)" -ForegroundColor Red return $null } } function Download-7z { param ( [string]$url ) $TEMP_DIR = $ENV:TEMP Write-Host 'Формируем путь для сохранения' -ForegroundColor Cyan Read-Host " 100 downloadUrl = $url" $installerFileName = Split-Path $url -Leaf Read-Host " 100 installerFileName = $installerFileName" $installerPath = Join-Path $TEMP_DIR $installerFileName Read-Host " 100 installerPath = $installerPath" Write-Host 'Скачиваем установщик 7z' -ForegroundColor Cyan try { Write-Host "Загрузка установщика: $url" -ForegroundColor Cyan Invoke-WebRequest -Uri $url -OutFile $installerPath -UseBasicParsing -ErrorAction Stop Write-Host "Установщик загружен: $installerPath" -ForegroundColor Cyan } catch { Write-Host "Ошибка загрузки: $($_.Exception.Message)" -ForegroundColor Red exit 1 } Write-Host 'Проверяем файл' -ForegroundColor Cyan if (!(Test-Path $installerPath)) { Write-Host "Файл установщика не найден: $installerPath" -ForegroundColor Red exit 1 } Write-Host 'Установочный файл успешно загружен. Продолжаем установку...' -ForegroundColor Cyan Write-Host 'Запускаем установку 7z' -ForegroundColor Cyan try { Start-Process -FilePath $installerPath -ArgumentList '/S' -Wait -ErrorAction Stop Write-Host '7-Zip успешно установлен.' -ForegroundColor Green } catch { Write-Host "Ошибка установки: $($_.Exception.Message)" -ForegroundColor Red exit 1 } finally { Write-Host 'Удаляем установочный файл' -ForegroundColor Cyan try { Remove-Item -Path $installerPath -ErrorAction Stop Write-Host 'Установочный файл удален.' -ForegroundColor Green } catch { Write-Host "Ошибка удаления установочного файла: $($_.Exception.Message)" -ForegroundColor Red } } } function install-7ip { try { $RESULT = $false if (-not (Test-Path 'C:\Program Files\7-Zip\7z.exe')) { Write-Host '7-Zip не найден. Устанавливаю 7-Zip...' -ForegroundColor Yellow $SEVENZIP_URL = 'https://gper.ru/soft/arc/7z' $FUrl = Get-Latest7zVersion -BaseUrl $SEVENZIP_URL if ($FUrl) { Write-Host "Скачиваю 7-Zip с URL: $FUrl" -ForegroundColor Cyan Read-Host " 202 download7zUrl = $FUrl" $RESULT = Download-7z -url $FUrl if ($RESULT) { Write-Host '7-Zip успешно установлен.' -ForegroundColor Green } else { Write-Host 'Не удалось скачать и установить 7-Zip.' -ForegroundColor Red } } else { Write-Host 'Не удалось найти ссылку для загрузки 7-Zip' -ForegroundColor Red return $RESULT } if (-not (Get-Module -ListAvailable -Name '7Zip4Powershell')) { Install-Module -Name '7Zip4Powershell' -Force -Scope CurrentUser } } else { Write-Host '7-Zip уже установлен.' -ForegroundColor Green } } catch { Write-Host "[X] Не удалось установить 7-Zip: $_" -ForegroundColor Red } finally { if (Test-Path 'C:\Program Files\7-Zip\7z.exe') { Write-Host 'Проверяю доступность 7-Zip...' -ForegroundColor Cyan try { & 'C:\Program Files\7-Zip\7z.exe' | Out-Null Write-Host '7-Zip доступен и готов к использованию.' -ForegroundColor Green } catch { Write-Host "Ошибка при проверке 7-Zip: $($_.Exception.Message)" -ForegroundColor Red } } else { Write-Host '7-Zip не найден после установки.' -ForegroundColor Red } } } function Test-Megafon { param ( [Parameter(Mandatory = $True)] [string]$ComputerName ) local $ROUTER = @{ IP = '192.168.12.1' PORT = 22 USER = 'tarakan' PASS = 'Sfnbrfy1983*' } Write-Host "Проверяю доступность роутера по SSH: $($ROUTER.IP):$($ROUTER.PORT)" -ForegroundColor Cyan try { $ssh = New-Object -TypeName System.Net.Sockets.TcpClient $ssh.Connect($ROUTER.IP, $ROUTER.PORT) if ($ssh.Connected) { Write-Host 'Роутер доступен по SSH.' -ForegroundColor Green $ssh.Close() return $true } else { Write-Host 'Роутер недоступен по SSH.' -ForegroundColor Red return $false } } catch { Write-Host "Ошибка при проверке роутера: $($_.Exception.Message)" -ForegroundColor Red return $false } try { Write-Host "Проверяю доступность компьютера $ComputerName по ICMP (ping)..." -ForegroundColor Cyan $ping = New-Object System.Net.NetworkInformation.Ping $reply = $ping.Send($ComputerName, 2000) # 2 секунды таймаут if ($reply.Status -eq 'Success') { Write-Host "Компьютер $ComputerName доступен по ping." -ForegroundColor Green return $true } else { Write-Host "Компьютер $ComputerName недоступен по ping: $($reply.Status)" -ForegroundColor Red return $false } } catch { Write-Host "Ошибка при проверке компьютера $($ComputerName): $($_.Exception.Message)" -ForegroundColor Red return $false } } # ================================================================ # Test-GperColorMapDuplicates # Версия: 1.0.0 | Дата: 2026-03-31 # Проверяет карту цветов на дублирующиеся слова между группами. # Вызывается при загрузке модуля и при расширении карты. # ================================================================ function Test-GperColorMapDuplicates { <# .SYNOPSIS Проверяет $Script:GPER_COLOR_MAP на дубликаты слов между цветами. .OUTPUTS [bool] $true если карта чистая, $false если есть дубли. .EXAMPLE Test-GperColorMapDuplicates #> $seen = @{} $clean = $true foreach ($color in $Script:GPER_COLOR_MAP.Keys) { foreach ($word in $Script:GPER_COLOR_MAP[$color]) { $w = $word.ToLower() if ($seen.ContainsKey($w)) { Write-Host "[!] Дубль в GPER_COLOR_MAP: '$w' → '$color' и '$($seen[$w])'" -ForegroundColor Yellow $clean = $false } else { $seen[$w] = $color } } } return $clean } # Проверяем при загрузке модуля $null = Test-GperColorMapDuplicates # ================================================================ # Resolve-GperColor # Версия: 1.0.0 | Дата: 2026-03-31 # Определяет цвет по тексту сообщения через GPER_COLOR_MAP. # Возвращает имя цвета или $null если совпадения нет. # ================================================================ function Resolve-GperColor { param([string]$Text) if ([string]::IsNullOrWhiteSpace($Text)) { return 'Red' } $lower = $Text.ToLower() foreach ($color in $Script:GPER_COLOR_MAP.Keys) { foreach ($word in $Script:GPER_COLOR_MAP[$color]) { if ($lower -match [regex]::Escape($word)) { return $color } } } return $null } # ================================================================ # Write-Host (переопределение) # Версия: 1.0.0 | Дата: 2026-03-31 # # ТОЛЬКО добавляет $GPER_FG_DEFAULT если ForegroundColor не указан. # Не анализирует текст это задача Show-Message. # Полностью совместимо со стандартным Write-Host. # ================================================================ function Write-Host { [CmdletBinding()] param( [Parameter(Position = 0, ValueFromPipeline = $true)] [object]$Object = '', [System.ConsoleColor]$ForegroundColor, [System.ConsoleColor]$BackgroundColor, [switch]$NoNewline, [switch]$Separator ) process { # Если цвет не передан явно используем дефолтный if (-not $PSBoundParameters.ContainsKey('ForegroundColor')) { $PSBoundParameters['ForegroundColor'] = $Script:GPER_FG_DEFAULT } if (-not $PSBoundParameters.ContainsKey('BackgroundColor') -and $Script:GPER_BG_DEFAULT) { $PSBoundParameters['BackgroundColor'] = $Script:GPER_BG_DEFAULT } Microsoft.PowerShell.Utility\Write-Host @PSBoundParameters } } # ================================================================ # Show-Message # Версия: 1.0.0 | Дата: 2026-03-31 # # Полноценная функция вывода с автоцветом по GPER_COLOR_MAP. # Если цвет явно указан он приоритетнее автоопределения. # ================================================================ function Show-Message { <# .SYNOPSIS Умный вывод сообщений с автоматическим цветом по ключевым словам. .DESCRIPTION Анализирует текст через GPER_COLOR_MAP и подбирает цвет. Если цвет указан явно используется он. Пустое сообщение → красный фон, текст-заглушка. .PARAMETER Message Текст сообщения. .PARAMETER ForegroundColor Явный цвет текста. Переопределяет автоопределение. .PARAMETER BackgroundColor Цвет фона. .PARAMETER NoNewline Не добавлять перенос строки. .EXAMPLE Show-Message "Ошибка подключения" # → Red Show-Message "Успешно установлено" # → Green Show-Message "Просто текст" # → White (fallback) Show-Message "Внимание!" -ForegroundColor Magenta # → Magenta (явный приоритет) #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$Message, [System.ConsoleColor]$ForegroundColor, [System.ConsoleColor]$BackgroundColor, [switch]$NoNewline ) # Пустое сообщение явная ошибка использования if ([string]::IsNullOrWhiteSpace($Message)) { Microsoft.PowerShell.Utility\Write-Host '[!] Show-Message: сообщение не указано' ` -ForegroundColor Red -BackgroundColor Black return } # Определяем цвет: явный → автоопределение → White (fallback) if (-not $PSBoundParameters.ContainsKey('ForegroundColor')) { $resolved = Resolve-GperColor -Text $Message $ForegroundColor = if ($resolved) { $resolved } else { 'White' } } $params = @{ Object = $Message; ForegroundColor = $ForegroundColor } if ($PSBoundParameters.ContainsKey('BackgroundColor')) { $params['BackgroundColor'] = $BackgroundColor } if ($NoNewline) { $params['NoNewline'] = $true } # Используем оригинальный Write-Host (не наш override) Microsoft.PowerShell.Utility\Write-Host @params } # ================================================================ # Show-Page # Версия: 1.0.0 | Дата: 2026-03-31 # # Баннер при загрузке модуля. ASCII-арт + минимум инфо. # Вызывается автоматически после Import-Module в $PROFILE. # ================================================================ function Show-Page { <# .SYNOPSIS Баннер GPER с ASCII-артом и инфо о сессии. .DESCRIPTION Выводит логотип GPER, версию модуля, версию PS, имя пользователя, компьютер и время. .EXAMPLE Show-Page #> [CmdletBinding()] param() $wh = { param($t, $c = 'DarkCyan') Microsoft.PowerShell.Utility\Write-Host $t -ForegroundColor DarkCyan } $v = $PSVersionTable.PSVersion $ps = if ($v.Patch) { "$($v.Major).$($v.Minor).$($v.Patch)" } else { "$($v.Major).$($v.Minor)" } $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) $adminMark = if ($isAdmin) { ' [Admin]' } else { '' } Microsoft.PowerShell.Utility\Write-Host '' & $wh ' ██████╗ ██████╗ ███████╗██████╗ ' & $wh ' ██╔════╝ ██╔══██╗██╔════╝██╔══██╗' & $wh ' ██║ ███╗██████╔╝█████╗ ██████╔╝' & $wh ' ██║ ██║██╔═══╝ ██╔══╝ ██╔══██╗' & $wh ' ██████╔╝██║ ███████╗██║ ██║' & $wh ' ╚════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ' 'DarkGray' Microsoft.PowerShell.Utility\Write-Host '' & $wh " v$Script:GPER_VERSION │ PS $ps$adminMark │ $env:COMPUTERNAME\$env:USERNAME" 'Cyan' & $wh " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') │ ps.gper.ru" 'DarkGray' Microsoft.PowerShell.Utility\Write-Host '' } # ================================================================ # WINGET обёртки # Версия: 1.0.0 | Дата: 2026-04-27 # ================================================================ function Test-WingetAvailable { return ($null -ne (Get-Command winget -ErrorAction SilentlyContinue)) } function wg { <# .SYNOPSIS Winget прямой вызов с проверкой установки. .EXAMPLE wg search git #> if (-not (Test-WingetAvailable)) { Microsoft.PowerShell.Utility\Write-Host '[X] winget не установлен' -ForegroundColor Red; return } & winget $args } function wgs { <# .SYNOPSIS winget search <запрос> .EXAMPLE wgs nodejs #> if (-not (Test-WingetAvailable)) { Microsoft.PowerShell.Utility\Write-Host '[X] winget не установлен' -ForegroundColor Red; return } & winget search $args } function wgu { <# .SYNOPSIS winget upgrade. Без аргументов обновляет все пакеты. .EXAMPLE wgu # все пакеты .EXAMPLE wgu Git.Git # конкретный #> if (-not (Test-WingetAvailable)) { Microsoft.PowerShell.Utility\Write-Host '[X] winget не установлен' -ForegroundColor Red; return } if ($args.Count -eq 0) { & winget upgrade --all } else { & winget upgrade $args } } function wgi { <# .SYNOPSIS winget install .EXAMPLE wgi Git.Git #> if (-not (Test-WingetAvailable)) { Microsoft.PowerShell.Utility\Write-Host '[X] winget не установлен' -ForegroundColor Red; return } & winget install $args } function wgrm { <# .SYNOPSIS winget uninstall .EXAMPLE wgrm Git.Git #> if (-not (Test-WingetAvailable)) { Microsoft.PowerShell.Utility\Write-Host '[X] winget не установлен' -ForegroundColor Red; return } & winget uninstall $args } # ================================================================ # Disable-UAC / Enable-UAC # Версия: 1.0.0 | Дата: 2026-04-27 # Реестр HKLM EnableLUA. Требует Admin + перезагрузки. # ================================================================ function Disable-UAC { <# .SYNOPSIS Отключить UAC. Требует прав администратора и перезагрузки. .EXAMPLE Disable-UAC #> $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Microsoft.PowerShell.Utility\Write-Host '[X] Требуются права администратора' -ForegroundColor Red; return } $key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' if ((Get-ItemProperty $key).EnableLUA -eq 0) { Microsoft.PowerShell.Utility\Write-Host '[!] UAC уже отключён' -ForegroundColor Yellow; return } Microsoft.PowerShell.Utility\Write-Host '[!] ВНИМАНИЕ: отключение UAC снижает безопасность системы!' -ForegroundColor Yellow Set-ItemProperty -Path $key -Name 'EnableLUA' -Value 0 -Type DWord Microsoft.PowerShell.Utility\Write-Host '[OK] UAC отключён. Требуется перезагрузка.' -ForegroundColor Green } function Enable-UAC { <# .SYNOPSIS Включить UAC. Требует прав администратора и перезагрузки. .EXAMPLE Enable-UAC #> $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Microsoft.PowerShell.Utility\Write-Host '[X] Требуются права администратора' -ForegroundColor Red; return } $key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' if ((Get-ItemProperty $key).EnableLUA -eq 1) { Microsoft.PowerShell.Utility\Write-Host '[!] UAC уже включён' -ForegroundColor Yellow; return } Set-ItemProperty -Path $key -Name 'EnableLUA' -Value 1 -Type DWord Microsoft.PowerShell.Utility\Write-Host '[OK] UAC включён. Требуется перезагрузка.' -ForegroundColor Green } # ================================================================ # Disable-AltTab / Enable-AltTab # Версия: 1.0.0 | Дата: 2026-04-27 # Убирает вкладки браузера из Alt+Tab (Windows 11 MultitaskingView). # Вступает в силу сразу, без перезагрузки. # ================================================================ function Disable-AltTab { <# .SYNOPSIS Убрать вкладки браузера из Alt+Tab (Windows 11). .EXAMPLE Disable-AltTab #> $key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MultitaskingView\AltTabViewHost' if (-not (Test-Path $key)) { New-Item -Path $key -Force | Out-Null } Set-ItemProperty -Path $key -Name 'GridHeight' -Value 0 -Type DWord Microsoft.PowerShell.Utility\Write-Host '[OK] Вкладки браузера в Alt+Tab отключены.' -ForegroundColor Green } function Enable-AltTab { <# .SYNOPSIS Вернуть стандартное поведение Alt+Tab (Windows 11). .EXAMPLE Enable-AltTab #> $key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MultitaskingView\AltTabViewHost' if (Test-Path $key) { Remove-ItemProperty -Path $key -Name 'GridHeight' -ErrorAction SilentlyContinue } Microsoft.PowerShell.Utility\Write-Host '[OK] Alt+Tab возвращён в стандартный режим.' -ForegroundColor Green } # ================================================================ # Update-Gper # Версия: 1.0.0 | Дата: 2026-04-27 # Скачивает свежий gper.psm1 с сервера, бэкапит текущий, # перезагружает модуль в текущем сеансе. # ================================================================ function Update-Gper { <# .SYNOPSIS Обновить модуль с сервера и перезагрузить в текущем сеансе. .EXAMPLE Update-Gper #> $url = "$Script:GPER_URL_PS/gper.psm1" $local = $Script:GPER_LOCAL_PSM1 $dir = $Script:GPER_LOCAL_DIR Microsoft.PowerShell.Utility\Write-Host '--- Обновление модуля...' -ForegroundColor Cyan try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $bytes = (New-Object System.Net.WebClient).DownloadData($url) } catch { Microsoft.PowerShell.Utility\Write-Host "[X] Не удалось скачать: $_" -ForegroundColor Red return } # Добавляем BOM если нет if (-not ($bytes.Count -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)) { $bytes = [byte[]](0xEF, 0xBB, 0xBF) + $bytes } # Бэкап текущей версии if (Test-Path $local) { $bak = Join-Path $dir "gper_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss').psm1" Copy-Item $local $bak -Force Microsoft.PowerShell.Utility\Write-Host "[bk] $(Split-Path $bak -Leaf)" -ForegroundColor DarkGray } [System.IO.File]::WriteAllBytes($local, $bytes) Microsoft.PowerShell.Utility\Write-Host "[OK] Сохранён: $local" -ForegroundColor Green try { Import-Module $local -Force -DisableNameChecking -ErrorAction Stop Microsoft.PowerShell.Utility\Write-Host '[OK] Модуль перезагружен.' -ForegroundColor Green Write-Host '[!] Проверяю профиль на загрузку новой версии...' -ForegroundColor Cyan $profile = $PROFILE if (Test-Path $profile) { . $profile Microsoft.PowerShell.Utility\Write-Host '[OK] Профиль перезагружен.' -ForegroundColor Green } else { Microsoft.PowerShell.Utility\Write-Host '[!] Профиль не найден' -ForegroundColor Red } } catch { Microsoft.PowerShell.Utility\Write-Host "[!] Не удалось перезагрузить: $_" -ForegroundColor Yellow } } # ================================================================ # gperm # Версия: 1.0.0 | Дата: 2026-04-27 # Запускает меню установки ПО (gperm.ps1) с сервера. # ================================================================ function gperm { <# .SYNOPSIS Меню установки ПО (iPXE-стиль). .EXAMPLE gperm #> [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $wc = New-Object System.Net.WebClient $wc.Encoding = [System.Text.Encoding]::UTF8 $script = $wc.DownloadString("$Script:GPER_URL_PS/gperm.ps1") Invoke-Expression $script } # ================================================================ # Invoke-GperInstall (alias: gperi, gper-install) # Версия: 1.0.0 | Дата: 2026-04-27 # Прямая установка пакета по короткому имени (без меню). # ================================================================ function Invoke-GperInstall { <# .SYNOPSIS Установка ПО по короткому имени. .EXAMPLE gperi winget .EXAMPLE gperi pwsh #> param( [Parameter(Mandatory, Position = 0)] [string]$Name ) # Реестр коротких имён → действие $registry = @{ 'winget' = @{ Type = 'Script'; Url = "$Script:GPER_URL_PS/ext/winget-install.ps1" } 'pwsh' = @{ Type = 'Winget'; Id = 'Microsoft.PowerShell' } 'terminal' = @{ Type = 'Winget'; Id = 'Microsoft.WindowsTerminal' } 'radmin' = @{ Type = 'Script'; Url = "$Script:GPER_URL_PS/ext/radmin-install.ps1" } 'radmin-reg' = @{ Type = 'Script'; Url = "$Script:GPER_URL_PS/ext/radmin-register.ps1" } 'vnc-reg' = @{ Type = 'Script'; Url = "$Script:GPER_URL_PS/vnc.ps1" } } $key = $Name.ToLower() if (-not $registry.ContainsKey($key)) { Write-Host "[X] Неизвестное имя: $Name" -ForegroundColor Red Write-Host "Доступно: $($registry.Keys -join ', ')" -ForegroundColor Yellow return } $entry = $registry[$key] [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Авто-перезапуск с правами администратора $isAdmin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host '[!] Требуются права администратора перезапуск...' -ForegroundColor Yellow $shell = if ($PSVersionTable.PSEdition -eq 'Core') { 'pwsh' } else { 'powershell' } $bootstrap = "[Net.ServicePointManager]::SecurityProtocol='Tls12'; " + "iex (New-Object Net.WebClient).DownloadString('$Script:GPER_URL_PS/gper.psm1'); " + "gperi $Name" try { Start-Process -FilePath $shell ` -ArgumentList '-NoExit', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', $bootstrap ` -Verb RunAs } catch { Write-Host "[X] Не удалось получить права администратора: $($_.Exception.Message)" -ForegroundColor Red } return } try { switch ($entry.Type) { 'Script' { Write-Host "[>] $Name ($($entry.Url))" -ForegroundColor Cyan $wc = New-Object System.Net.WebClient $wc.Encoding = [System.Text.Encoding]::UTF8 $code = $wc.DownloadString($entry.Url) Invoke-Expression $code } 'Winget' { Write-Host "[>] $Name (winget: $($entry.Id))" -ForegroundColor Cyan & winget install -e --id $entry.Id --accept-source-agreements --accept-package-agreements } } Write-Host "[OK] Готово: $Name" -ForegroundColor Green } catch { Write-Host "[X] Ошибка: $($_.Exception.Message)" -ForegroundColor Red Write-Host $_.ScriptStackTrace -ForegroundColor DarkGray } } # ================================================================ # Find-Pattern (alias: fp) # Версия: 1.0.0 | Дата: 2026-05-18 # Многословный поиск (логическое И) в текстовых файлах # с подсветкой совпадений, фильтрацией по размеру/типу # и защитой от Base64/слишком длинных строк. # ================================================================ function Find-Pattern { <# .SYNOPSIS Поиск нескольких слов (логическое И) в текстовых файлах с подсветкой. .DESCRIPTION Рекурсивно ищет в файлах строки, в которых одновременно встречаются ВСЕ указанные шаблоны (логическое И через lookahead-регэксп). Подсвечивает найденные слова красным (ANSI). Пропускает бинарный мусор: слишком длинные строки и Base64-подобные блоки. Фильтрует по размеру файла. .PARAMETER Path Каталог поиска. По умолчанию текущий каталог. .PARAMETER Patterns Один или несколько шаблонов. Все должны присутствовать в строке. Шаблоны экранируются спецсимволы regex не нужны. .PARAMETER Include Маски имён файлов. По умолчанию: *.ps1, *.psm1, *.psd1, *.txt, *.md, *.json, *.yml, *.yaml, *.cmd, *.sh, *.conf, *.ini .PARAMETER MaxFileSize Максимальный размер файла (строка вида '1MB', '500KB'). По умолчанию 1MB. .PARAMETER MaxLineLength Строки длиннее пропускаются. По умолчанию 1000. .PARAMETER Format Формат вывода: Table (по умолчанию) или List. .PARAMETER ShowFullPath В таблице показывать полный путь вместо имени файла. .PARAMETER DebugMode Показывать прогресс и причины пропуска строк. .EXAMPLE fp -Patterns 'function','Show-Page' # ↑ оба слова в одной строке, по текущему каталогу .EXAMPLE fp 'TODO' -Path l:\Development -Include *.ps1,*.psm1 .EXAMPLE Find-Pattern -Patterns 'password','plain' -Format List -ShowFullPath .EXAMPLE fp 'docker','restart' -MaxFileSize 5MB -DebugMode .NOTES Замена сломанному Search-TextContent_V02.ps1. #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string[]]$Patterns, [Parameter(Position = 1)] [string]$Path = '.', [string[]]$Include = @( '*.ps1', '*.psm1', '*.psd1', '*.txt', '*.md', '*.json', '*.yml', '*.yaml', '*.cmd', '*.sh', '*.conf', '*.ini' ), [string]$MaxFileSize = '1MB', [int]$MaxLineLength = 1000, [ValidateSet('Table', 'List')] [string]$Format = 'Table', [switch]$ShowFullPath, [switch]$DebugMode ) # Парсим размер try { $maxBytes = [scriptblock]::Create($MaxFileSize).Invoke()[0] } catch { Microsoft.PowerShell.Utility\Write-Host "[X] Неверный MaxFileSize: $MaxFileSize" -ForegroundColor Red; return } if (-not (Test-Path $Path)) { Microsoft.PowerShell.Utility\Write-Host "[X] Путь не найден: $Path" -ForegroundColor Red return } # Регэксп: AND через lookahead $regex = '' foreach ($p in $Patterns) { $regex += "(?=.*$([regex]::Escape($p)))" } $regex += '.*' # Регэксп для подсветки (любой из шаблонов) $hlRegex = '(' + (($Patterns | ForEach-Object { [regex]::Escape($_) }) -join '|') + ')' $esc = [char]27 $red = "$esc[91m" $reset = "$esc[0m" $results = New-Object System.Collections.Generic.List[object] $files = Get-ChildItem -Path $Path -Recurse -File -Include $Include -ErrorAction SilentlyContinue | Where-Object { $_.Length -lt $maxBytes } foreach ($file in $files) { $displaySize = if ($file.Length -ge 1MB) { '{0:N2} MB' -f ($file.Length / 1MB) } else { '{0:N2} KB' -f ($file.Length / 1KB) } if ($DebugMode) { Microsoft.PowerShell.Utility\Write-Host "`r[?] $displaySize | $($file.Name)".PadRight(100) -NoNewline -ForegroundColor DarkGray } $lineNumber = 0 try { $content = Get-Content -LiteralPath $file.FullName -ErrorAction Stop } catch { continue } foreach ($currentLine in $content) { $lineNumber++ if ($null -eq $currentLine) { continue } if ($currentLine.Length -gt $MaxLineLength) { if ($DebugMode) { Microsoft.PowerShell.Utility\Write-Host " $($file.Name):$lineNumber длинная строка, пропуск" -ForegroundColor DarkGray } continue } if ($currentLine -match '[A-Za-z0-9+/]{100,}') { if ($DebugMode) { Microsoft.PowerShell.Utility\Write-Host " $($file.Name):$lineNumber Base64-подобная, пропуск" -ForegroundColor DarkGray } continue } if ($currentLine -match $regex) { $highlighted = [regex]::Replace($currentLine, $hlRegex, "$red`$1$reset") $results.Add([PSCustomObject]@{ File = if ($ShowFullPath) { $file.FullName } else { $file.Name } Line = $lineNumber Size = $displaySize Text = $highlighted.Trim() Path = $file.FullName }) } } } if ($DebugMode) { Microsoft.PowerShell.Utility\Write-Host ("`r" + (' ' * 100) + "`r") -NoNewline } if ($results.Count -eq 0) { Microsoft.PowerShell.Utility\Write-Host '[!] Ничего не найдено.' -ForegroundColor Yellow return } Microsoft.PowerShell.Utility\Write-Host "[OK] Найдено совпадений: $($results.Count) в файлах: $((($results | Select-Object -Unique Path).Count))" -ForegroundColor Green if ($Format -eq 'Table') { $results | Format-Table File, Line, Size, Text -AutoSize -Wrap } else { $results | Format-List File, Line, Size, Path, Text } } function Unlock-Vault { # cls . L:\Development\009-Self-host\0003-Gper-Project\Secret-Manager\01-Create-SecretVault.ps1 $BW_URL = $MYSECRETMANAGEMENT.MYSECRET.MYVAULTWARDEN.URL $ENV_VAR_NAME = $MYSECRETMANAGEMENT.MYSECRET.NAME $ENV_VAR_TYPE = $MYSECRETMANAGEMENT.MYSECRET.TYPE $ENV_VAR_VALUE = [System.Environment]::GetEnvironmentVariable($ENV_VAR_NAME, [EnvironmentVariableTarget]::User) bw config server "$BW_URL" | Out-Null $env:BW_PASSWORD = Microsoft.PowerShell.SecretManagement\Get-Secret -Vault "$($MYSECRETMANAGEMENT.MYSECRET.VAULTNAME)" -Name "$($ENV_VAR_NAME)" -AsPlainText # [System.Environment]::SetEnvironmentVariable('BW_TMP',"$($env:BW_PASSWORD)",[EnvironmentVariableTarget]::User) $env:BW_SESSION = (bw unlock --passwordenv BW_PASSWORD --raw) $env:BW_PASSWORD = $null $FSTATUS = (bw status | ConvertFrom-Json | Select-Object -Property status) if ( "$($FSTATUS.status)" -eq 'unlocked' ) { Write-Host 'Хранилище разблокировано' -ForegroundColor Green } else { Write-Host 'Хранилище заблокировано' -ForegroundColor Green } $env:BW_PASSWORD = [System.Environment]::GetEnvironmentVariable('BW_TMP', [EnvironmentVariableTarget]::User) } # ================================================================ # Invoke-Notepad открыть файл в Notepad++ если установлен, иначе в notepad # ================================================================ function Invoke-Notepad { [CmdletBinding()] param( [Parameter(ValueFromRemainingArguments = $true, Position = 0)] [string[]]$Files ) if (-not $Script:GPER_NOTEPAD_EXE) { $cmd = Get-Command 'notepad++.exe' -ErrorAction SilentlyContinue | Select-Object -First 1 $found = @( if ($cmd) { $cmd.Source } "$env:ProgramFiles\Notepad++\notepad++.exe" "${env:ProgramFiles(x86)}\Notepad++\notepad++.exe" ) | Where-Object { $_ -and (Test-Path -LiteralPath $_) } | Select-Object -First 1 $Script:GPER_NOTEPAD_EXE = if ($found) { $found } else { "$env:WINDIR\System32\notepad.exe" } } if ($Files) { & $Script:GPER_NOTEPAD_EXE @Files } else { & $Script:GPER_NOTEPAD_EXE } } # Добавить DNS-запись на оба Pi-hole через API v6 (обходит wildcard address=/gper.ru/) function Add-PiholeException { param( [Parameter(Mandatory)][string]$Domain, [Parameter(Mandatory)][string]$IP ) $sshKey = 'C:\Users\tarakan\.ssh\ai_ssh_key' $pyScript = @" import urllib.request, urllib.parse, json, sys domain, ip = "$Domain", "$IP" record = f"{ip} {domain}" url = "http://localhost:8081" req = urllib.request.Request(f"{url}/api/auth", data=json.dumps({"password":"Sfnbrfy1983*"}).encode(), headers={"Content-Type":"application/json"}, method="POST") sid = json.loads(urllib.request.urlopen(req).read())["session"]["sid"] req2 = urllib.request.Request(f"{url}/api/config/dns/hosts", headers={"X-FTL-SID": sid}) hosts = json.loads(urllib.request.urlopen(req2).read())["config"]["dns"]["hosts"] if record in hosts: print(f"Уже есть: {record}") sys.exit(0) enc = urllib.parse.quote(record, safe="") req3 = urllib.request.Request(f"{url}/api/config/dns/hosts/{enc}", headers={"X-FTL-SID": sid}, method="PUT") r = urllib.request.urlopen(req3) print(f"Добавлено [{r.status}]: {record}") "@ foreach ($pihole in @('192.168.100.9', '192.168.100.31')) { $result = $pyScript | ssh -i $sshKey -o StrictHostKeyChecking=no "root@$pihole" 'python3 -' Write-Host "[$pihole] $result" } # Сброс кэша Keenetic (PID определяется динамически) ipconfig /flushdns | Out-Null $ndnPid = plink -pw sfnbrfy -batch -P 222 root@192.168.100.1 "ps | grep ndnproxymain | grep -v grep | awk '{print `$1}' | head -1" if ($ndnPid) { plink -pw sfnbrfy -batch -P 222 root@192.168.100.1 "kill -HUP $ndnPid" 2>$null } Start-Sleep 2 ipconfig /flushdns | Out-Null $resolved = (Resolve-DnsName $Domain -Type A -ErrorAction SilentlyContinue).IPAddress if ($resolved -eq $IP) { Write-Host "$Domain -> $resolved [OK]" -ForegroundColor Green } else { Write-Host "$Domain -> $resolved [MISMATCH, ожидался $IP]" -ForegroundColor Red } } function Del-PiholeException { param( [Parameter(Mandatory)][string]$Domain, [Parameter(Mandatory)][string]$IP ) $sshKey = 'C:\Users\tarakan\.ssh\ai_ssh_key' $pyScript = @" import urllib.request, urllib.parse, json, sys domain, ip = "$Domain", "$IP" record = f"{ip} {domain}" url = "http://localhost:8081" req = urllib.request.Request(f"{url}/api/auth", data=json.dumps({"password":"Sfnbrfy1983*"}).encode(), headers={"Content-Type":"application/json"}, method="POST") sid = json.loads(urllib.request.urlopen(req).read())["session"]["sid"] req2 = urllib.request.Request(f"{url}/api/config/dns/hosts", headers={"X-FTL-SID": sid}) hosts = json.loads(urllib.request.urlopen(req2).read())["config"]["dns"]["hosts"] if record not in hosts: print(f"Не найдено: {record}") sys.exit(0) enc = urllib.parse.quote(record, safe="") req3 = urllib.request.Request(f"{url}/api/config/dns/hosts/{enc}", headers={"X-FTL-SID": sid}, method="DELETE") r = urllib.request.urlopen(req3) print(f"Удалено [{r.status}]: {record}") "@ foreach ($pihole in @('192.168.100.9', '192.168.100.31')) { $result = $pyScript | ssh -i $sshKey -o StrictHostKeyChecking=no "root@$pihole" 'python3 -' Write-Host "[$pihole] $result" } # Сброс кэша Keenetic ipconfig /flushdns | Out-Null $ndnPid = plink -pw sfnbrfy -batch -P 222 root@192.168.100.1 "ps | grep ndnproxymain | grep -v grep | awk '{print `$1}' | head -1" if ($ndnPid) { plink -pw sfnbrfy -batch -P 222 root@192.168.100.1 "kill -HUP $ndnPid" 2>$null } Start-Sleep 2 ipconfig /flushdns | Out-Null $resolved = (Resolve-DnsName $Domain -Type A -ErrorAction SilentlyContinue).IPAddress if ($resolved -ne $IP) { Write-Host "$Domain больше не резолвится в $IP [OK]" -ForegroundColor Green } else { Write-Host "$Domain всё ещё резолвится в $resolved [MISMATCH]" -ForegroundColor Red } } function Set-ENV { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Name, [Parameter()] [string]$Value, # Гарантирует преобразование в строку [Parameter()] [ValidateSet('Process', 'User', 'Machine')] $Scope = 'User' # Автоматически приведется к нужному типу ) [Environment]::SetEnvironmentVariable($Name, $Value, $Scope) } function Get-ENV { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Name, [Parameter()] [ValidateSet('Process', 'User', 'Machine')] $Scope = 'User' # Автоматически приведется к нужному типу ) [Environment]::GetEnvironmentVariable($Name, $Scope) } # ================================================================ # Реестр хостов для WOL # Версия: 1.0.0 | Дата: 2026-06-04 # Реестр WOL-хостов хранится в Firebird БД gper.fdb, таблица WOL_HOSTS. # Доступ через модуль gper-db.psm1 (импортируется автоматически). # CRUD: Add-GperWolHost | Get-GperWolHost | Set-GperWolHost | Remove-GperWolHost # Алиасы под привычные имена: # Add-GPER_WOL_HOSTS / Get-GPER_WOL_HOSTS / Set-GPER_WOL_HOSTS / Remove-GPER_WOL_HOSTS # ================================================================ # ---------------------------------------------------------------- # Внутренние помощники # ---------------------------------------------------------------- function script:Ensure-GperDb { if (Get-Command Send-ToDb -ErrorAction SilentlyContinue) { return } $p = Join-Path $env:APPDATA 'gper.ru\gper-db.psm1' if (-not (Test-Path $p)) { throw "gper-db.psm1 не найден ($p). Установи: irm https://ps.gper.ru/db/gper-db.psm1 | iex" } Import-Module $p -Force -DisableNameChecking -Global -ErrorAction Stop } function script:ConvertTo-NormalizedMac { param([string]$Mac) if (-not $Mac) { return $null } $hex = ($Mac -replace '[:\-\.\s]', '').ToUpper() if ($hex.Length -ne 12 -or $hex -notmatch '^[0-9A-F]{12}$') { return $null } return (0..5 | ForEach-Object { $hex.Substring($_ * 2, 2) }) -join ':' } function script:Test-IpV4 { param([string]$Ip) if (-not $Ip) { return $true } # пустой допустим if ($Ip -notmatch '^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$') { return $false } foreach ($o in ($Ip -split '\.')) { if ([int]$o -gt 255) { return $false } } return $true } function script:Get-WolHostByField { param([string]$Field, [string]$Value) script:Ensure-GperDb # Если таблицы ещё нет — нечего и искать Open-GperConnection try { if (-not (Test-FbTable 'WOL_HOSTS')) { return $null } } finally { Close-GperConnection } $v = $Value -replace "'", "''" return Get-FromDb -TableName 'WOL_HOSTS' -Filter "UPPER($Field) = UPPER('$v')" -Limit 1 } # ================================================================ # Add-GperWolHost # ================================================================ function Add-GperWolHost { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Name, [Parameter(Mandatory, Position = 1)] [string]$Mac, [string]$Ip, [string]$Broadcast = '192.168.100.255', [string]$Desc = '' ) script:Ensure-GperDb $macN = script:ConvertTo-NormalizedMac $Mac if (-not $macN) { Write-Host "[X] Некорректный MAC: '$Mac'" -ForegroundColor Red; return } if (-not (script:Test-IpV4 $Ip)) { Write-Host "[X] Некорректный IP: '$Ip'" -ForegroundColor Red; return } if (-not (script:Test-IpV4 $Broadcast)) { Write-Host "[X] Некорректный Broadcast: '$Broadcast'" -ForegroundColor Red; return } if (script:Get-WolHostByField 'HNAME' $Name) { Write-Host "[X] Имя '$Name' уже занято." -ForegroundColor Red; return } if (script:Get-WolHostByField 'MAC' $macN) { Write-Host "[X] MAC '$macN' уже в реестре." -ForegroundColor Red; return } Send-ToDb @{ DbName = 'gperdb' WOL_HOSTS = @{ HNAME = $Name MAC = $macN IP = $Ip BROADCAST = $Broadcast DESCR = $Desc UPDATEDAT = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') } } | Out-Null Write-Host "[OK] Добавлен '$Name' → $macN" -ForegroundColor Green } # ================================================================ # Get-GperWolHost [-Name|-Mac|-Ip|-List] # ================================================================ function Get-GperWolHost { [CmdletBinding(DefaultParameterSetName = 'ByName')] param( [Parameter(ParameterSetName = 'ByName', Position = 0)] [string]$Name, [Parameter(ParameterSetName = 'ByMac')] [string]$Mac, [Parameter(ParameterSetName = 'ByIp')] [string]$Ip, [Parameter(ParameterSetName = 'List')] [switch]$List ) script:Ensure-GperDb switch ($PSCmdlet.ParameterSetName) { 'List' { Open-GperConnection try { if (-not (Test-FbTable 'WOL_HOSTS')) { Write-Host "Таблица WOL_HOSTS ещё не создана." -ForegroundColor Yellow; return @() } } finally { Close-GperConnection } return Get-FromDb -TableName 'WOL_HOSTS' -Limit 1000 | Sort-Object HNAME | Select-Object HNAME, MAC, IP, BROADCAST, DESCR } 'ByMac' { $macN = script:ConvertTo-NormalizedMac $Mac if (-not $macN) { Write-Host "[X] Некорректный MAC: '$Mac'" -ForegroundColor Red; return } return script:Get-WolHostByField 'MAC' $macN } 'ByIp' { return script:Get-WolHostByField 'IP' $Ip } default { if (-not $Name) { return Get-GperWolHost -List } return script:Get-WolHostByField 'HNAME' $Name } } } # ================================================================ # Set-GperWolHost — изменить поля существующего хоста (ключ: Name) # ================================================================ function Set-GperWolHost { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Name, [string]$Mac, [string]$Ip, [string]$Broadcast, [string]$Desc ) script:Ensure-GperDb $current = script:Get-WolHostByField 'HNAME' $Name if (-not $current) { Write-Host "[X] Не найден хост '$Name'" -ForegroundColor Red; return } $sets = [ordered]@{} if ($PSBoundParameters.ContainsKey('Mac')) { $macN = script:ConvertTo-NormalizedMac $Mac if (-not $macN) { Write-Host "[X] Некорректный MAC: '$Mac'" -ForegroundColor Red; return } $dup = script:Get-WolHostByField 'MAC' $macN if ($dup -and $dup.HNAME -ne $current.HNAME) { Write-Host "[X] MAC '$macN' уже у '$($dup.HNAME)'" -ForegroundColor Red; return } $sets['MAC'] = $macN } if ($PSBoundParameters.ContainsKey('Ip')) { if (-not (script:Test-IpV4 $Ip)) { Write-Host "[X] Некорректный IP: '$Ip'" -ForegroundColor Red; return } $sets['IP'] = $Ip } if ($PSBoundParameters.ContainsKey('Broadcast')) { if (-not (script:Test-IpV4 $Broadcast)) { Write-Host "[X] Некорректный Broadcast: '$Broadcast'" -ForegroundColor Red; return } $sets['BROADCAST'] = $Broadcast } if ($PSBoundParameters.ContainsKey('Desc')) { $sets['DESCR'] = $Desc } if ($sets.Count -eq 0) { Write-Host "Нет полей для изменения." -ForegroundColor Yellow; return } $sets['UPDATEDAT'] = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Open-GperConnection try { $assign = ($sets.Keys | ForEach-Object { "$_ = @$_" }) -join ', ' $sql = "UPDATE WOL_HOSTS SET $assign WHERE UPPER(HNAME) = UPPER(@HNAME)" $prm = @{} + $sets $prm['HNAME'] = $Name Invoke-FbNonQuery -Sql $sql -Params $prm Write-Host "[OK] Обновлён '$Name': $($sets.Keys -join ', ')" -ForegroundColor Green } finally { Close-GperConnection } } # ================================================================ # Remove-GperWolHost # ================================================================ function Remove-GperWolHost { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param([Parameter(Mandatory, Position = 0)] [string]$Name) script:Ensure-GperDb $h = script:Get-WolHostByField 'HNAME' $Name if (-not $h) { Write-Host "[X] Не найден '$Name'" -ForegroundColor Red; return } $safe = $Name -replace "'", "''" Remove-FromDb -TableName 'WOL_HOSTS' -Filter "UPPER(HNAME) = UPPER('$safe')" } # Алиасы под привычные имена пользователя Set-Alias -Name Add-GPER_WOL_HOSTS -Value Add-GperWolHost Set-Alias -Name Get-GPER_WOL_HOSTS -Value Get-GperWolHost Set-Alias -Name Set-GPER_WOL_HOSTS -Value Set-GperWolHost Set-Alias -Name Remove-GPER_WOL_HOSTS -Value Remove-GperWolHost # ================================================================ # Send-WakeOnLan # Версия: 2.0.0 | Дата: 2026-06-04 # Резолв алиаса через БД (gper-db). Если БД недоступна — пробуем как MAC. # ================================================================ function Send-WakeOnLan { <# .SYNOPSIS Послать magic packet для пробуждения хоста по WOL. .DESCRIPTION Target — алиас хоста из БД (WOL_HOSTS) или MAC-адрес. Без аргументов — выводит список из БД. .EXAMPLE wol pve-home .EXAMPLE wol 74:56:3c:39:a6:96 .EXAMPLE wol #> [CmdletBinding()] param ( [Parameter(Position = 0)] [string]$Target, [Parameter(Position = 1)] [string]$Broadcast, [ValidateSet(7, 9)] [int]$Port = 9, [int]$Count = 3 ) if (-not $Target) { Get-GperWolHost -List | Format-Table -AutoSize; return } $mac = $null try { $h = Get-GperWolHost -Name $Target if ($h) { $mac = $h.MAC if (-not $Broadcast -and $h.BROADCAST) { $Broadcast = $h.BROADCAST } } } catch { Write-Host "[!] БД недоступна: $($_.Exception.Message)" -ForegroundColor DarkYellow } if (-not $mac) { $mac = $Target } if (-not $Broadcast) { $Broadcast = '192.168.100.255' } # Нормализация MAC: убираем разделители, проверяем длину $hex = ($mac -replace '[:\-\.\s]', '').ToUpper() if ($hex.Length -ne 12 -or $hex -notmatch '^[0-9A-F]{12}$') { Write-Host "[X] Не найден алиас и не похоже на MAC: '$Target'" -ForegroundColor Red Write-Host ' Используй wol без аргументов для списка известных хостов.' -ForegroundColor DarkGray return } $bytes = for ($i = 0; $i -lt 12; $i += 2) { [byte]([Convert]::ToInt32($hex.Substring($i, 2), 16)) } # Magic packet: 6 × 0xFF + 16 × MAC $packet = (, [byte]0xFF * 6) + ($bytes * 16) $udp = [System.Net.Sockets.UdpClient]::new() try { $udp.EnableBroadcast = $true for ($i = 0; $i -lt $Count; $i++) { $udp.Send($packet, $packet.Length, $Broadcast, $Port) | Out-Null } $macFmt = (0..5 | ForEach-Object { $hex.Substring($_ * 2, 2) }) -join ':' Write-Host "[OK] Magic packet → $Target ($macFmt) на ${Broadcast}:$Port (x$Count)" -ForegroundColor Green } catch { Write-Host "[X] Ошибка отправки WOL: $($_.Exception.Message)" -ForegroundColor Red } finally { $udp.Close() } } # Алиасы Set-Alias -Name so -Value Select-Object Set-Alias -Name sm -Value Show-Message Set-Alias -Name gh -Value Get-Help Set-Alias -Name wh -Value Write-Host Set-Alias -Name tp -Value Test-Path Set-Alias -Name np -Value Invoke-Notepad Set-Alias -Name fp -Value Find-Pattern Set-Alias -Name gperi -Value Invoke-GperInstall Set-Alias -Name gper-install -Value Invoke-GperInstall Set-Alias -Name wol -Value Send-WakeOnLan # ================================================================ # РЕЕСТР ФУНКЦИЙ МОДУЛЯ # Обновлять при добавлении каждой новой функции # ================================================================ $Script:GPER_FUNCTIONS = @( @{ Name = 'Invoke-Notepad'; Version = '1.0.0'; Date = '2026-05-18'; Desc = 'Открыть в Notepad++ (если есть) или notepad, alias: np' } @{ Name = 'Write-Host'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Переопределение: дефолтный цвет Cyan' } @{ Name = 'Show-Message'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Умный вывод с автоцветом' } @{ Name = 'Show-Page'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Баннер GPER с ASCII-артом' } @{ Name = 'Test-GperColorMapDuplicates'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Проверка карты цветов на дубли' } @{ Name = 'Resolve-GperColor'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Определение цвета по тексту' } @{ Name = 'Gper-List'; Version = '1.0.0'; Date = '2026-03-31'; Desc = 'Список функций модуля' } @{ Name = 'wg'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'winget прямой вызов' } @{ Name = 'wgs'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'winget search' } @{ Name = 'wgu'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'winget upgrade [id|--all]' } @{ Name = 'wgi'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'winget install' } @{ Name = 'wgrm'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'winget uninstall' } @{ Name = 'Disable-UAC'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Отключить UAC (Admin + reboot)' } @{ Name = 'Enable-UAC'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Включить UAC (Admin + reboot)' } @{ Name = 'Disable-AltTab'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Убрать вкладки браузера из Alt+Tab (Win11)' } @{ Name = 'Enable-AltTab'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Вернуть стандартный Alt+Tab (Win11)' } @{ Name = 'Update-Gper'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Обновить модуль с сервера + бэкап' } @{ Name = 'gperm'; Version = '1.0.0'; Date = '2026-04-27'; Desc = 'Меню установки ПО (iPXE-стиль)' } @{ Name = 'Find-Pattern'; Version = '1.0.0'; Date = '2026-05-18'; Desc = 'Поиск нескольких слов (И) в файлах, alias: fp' } @{ Name = 'Add-PiholeException'; Version = '1.0.0'; Date = '2026-05-18'; Desc = '' } @{ Name = 'Del-PiholeException'; Version = '1.0.0'; Date = '2026-05-18'; Desc = '' } @{ Name = 'Install-GPER_REQUIREMENTS'; Version = '1.0.0'; Date = '2026-05-23'; Desc = '' } @{ Name = 'Unlock-Vault'; Version = '1.0.0'; Date = '2026-06-01'; Desc = 'Разблокировать хранилище Bitwarden и установить сессию в env:BW_SESSION' } @{ Name = 'Install-7Zip'; Version = '1.0.0'; Date = '2026-06-01'; Desc = 'Установка 7-Zip через winget' } @{ Name = 'Test-Megafon'; Version = '1.0.0'; Date = '2026-06-01'; Desc = 'Проверка наличия связи с сервером Мегафон' } @{ Name = 'Send-WakeOnLan'; Version = '2.0.0'; Date = '2026-06-04'; Desc = 'WOL: алиас из БД WOL_HOSTS или MAC, alias: wol' } @{ Name = 'Add-GperWolHost'; Version = '1.0.0'; Date = '2026-06-04'; Desc = 'Добавить хост в БД WOL_HOSTS (alias: Add-GPER_WOL_HOSTS)' } @{ Name = 'Get-GperWolHost'; Version = '1.0.0'; Date = '2026-06-04'; Desc = 'Найти/список WOL-хостов [-Name|-Mac|-Ip|-List]' } @{ Name = 'Set-GperWolHost'; Version = '1.0.0'; Date = '2026-06-04'; Desc = 'Изменить поля WOL-хоста' } @{ Name = 'Remove-GperWolHost'; Version = '1.0.0'; Date = '2026-06-04'; Desc = 'Удалить WOL-хост из БД' } ) function Gper-List { <# .SYNOPSIS Список всех функций модуля с версиями и датами. .EXAMPLE gperlist #> Microsoft.PowerShell.Utility\Write-Host '' Microsoft.PowerShell.Utility\Write-Host " GPER Module v$Script:GPER_VERSION ($Script:GPER_DATE)" -ForegroundColor Cyan Microsoft.PowerShell.Utility\Write-Host '' $Script:GPER_FUNCTIONS | ForEach-Object { [PSCustomObject]$_ } | Format-Table Name, Version, Date, Desc -AutoSize }