############################################################# #### ntfy Desktop Client Setup for Windows #### #### Subscribe to ntfy push notifications with Windows #### #### toast notifications #### #### #### #### Author: Phil Connor #### #### Contact: contact@mylinux.work #### #### License: MIT #### #### Version: 1.0 #### #### #### #### Usage: .\ntfy-client-setup-windows.ps1 #### ############################################################# $ErrorActionPreference = "Stop" # --- Configuration --- $NtfyVersion = "2.8.0" $InstallDir = "$env:LOCALAPPDATA\ntfy" $ConfigDir = "$env:APPDATA\ntfy" # --- Interactive Prompts --- Write-Host "" Write-Host "=== ntfy Desktop Notifications Setup ===" -ForegroundColor Cyan Write-Host "Installing for user: $env:USERNAME" Write-Host "" # Server URL $ServerUrl = Read-Host "Enter your ntfy server URL (e.g. https://ntfy.example.com)" $ServerUrl = $ServerUrl.TrimEnd("/") if ([string]::IsNullOrWhiteSpace($ServerUrl)) { Write-Host "ERROR: Server URL is required." -ForegroundColor Red exit 1 } # Access token (optional — some servers allow anonymous access) $Token = Read-Host "Enter your access token (leave blank if not required)" # Topics $topicInput = Read-Host "Enter topics to subscribe to, comma-separated (e.g. alerts-critical,alerts-all)" if ([string]::IsNullOrWhiteSpace($topicInput)) { Write-Host "ERROR: At least one topic is required." -ForegroundColor Red exit 1 } $Topics = $topicInput -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } Write-Host "" Write-Host "Server: $ServerUrl" -ForegroundColor White Write-Host "Topics: $($Topics -join ', ')" -ForegroundColor White Write-Host "Token: $(if ($Token) { '********' } else { '(none)' })" -ForegroundColor White Write-Host "" # --- Create directories --- New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null New-Item -ItemType Directory -Force -Path $ConfigDir | Out-Null # --- Download ntfy if not already installed --- if (Test-Path "$InstallDir\ntfy.exe") { Write-Host "ntfy already installed at: $InstallDir\ntfy.exe" -ForegroundColor Green } else { Write-Host "Downloading ntfy v$NtfyVersion..." $downloadUrl = "https://github.com/binwiederhier/ntfy/releases/download/v$NtfyVersion/ntfy_${NtfyVersion}_windows_amd64.zip" $zipPath = "$env:TEMP\ntfy.zip" Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath Write-Host "Extracting..." $extractPath = "$env:TEMP\ntfy_extract" Remove-Item -Path $extractPath -Recurse -Force -ErrorAction SilentlyContinue Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force Remove-Item $zipPath # Find the exe (may be in a subfolder) $ntfyExe = Get-ChildItem -Path $extractPath -Recurse -Filter "ntfy.exe" | Select-Object -First 1 if ($ntfyExe) { Copy-Item -Path $ntfyExe.FullName -Destination "$InstallDir\ntfy.exe" -Force } else { Write-Host "ERROR: Could not find ntfy.exe in downloaded archive." -ForegroundColor Red exit 1 } Remove-Item -Path $extractPath -Recurse -Force -ErrorAction SilentlyContinue Write-Host "Installed to: $InstallDir\ntfy.exe" -ForegroundColor Green } Write-Host "" # --- Create client.yml config --- $clientYml = @" default-host: $ServerUrl "@ if ($Token) { $clientYml += "`ndefault-token: $Token" } $clientYmlPath = "$ConfigDir\client.yml" $clientYml | Out-File -FilePath $clientYmlPath -Encoding UTF8 Write-Host "Client config saved to: $clientYmlPath" -ForegroundColor Green # --- Build topic URLs --- $topicUrls = @() foreach ($topic in $Topics) { $topicUrls += "$ServerUrl/$topic" } $topicUrlsString = $topicUrls -join " " # --- Create PowerShell notification script --- # Build the token environment line only if a token was provided $tokenLine = "" if ($Token) { $tokenLine = "`$env:NTFY_TOKEN = `"$Token`"" } $psScriptContent = @" Add-Type -AssemblyName System.Windows.Forms # Create a persistent notification icon in the system tray `$global:notifyIcon = New-Object System.Windows.Forms.NotifyIcon `$global:notifyIcon.Icon = [System.Drawing.SystemIcons]::Information `$global:notifyIcon.Visible = `$true `$global:notifyIcon.Text = "ntfy alerts" function Show-Notification { param([string]`$Title, [string]`$Message, [int]`$Priority) # Map ntfy priority levels to Windows balloon icon types # 1 (min), 2 (low) -> None # 3 (default) -> Info # 4 (high), 5 (max) -> Error `$icon = [System.Windows.Forms.ToolTipIcon]::Info if (`$Priority -ge 4) { `$icon = [System.Windows.Forms.ToolTipIcon]::Error } elseif (`$Priority -le 2) { `$icon = [System.Windows.Forms.ToolTipIcon]::None } `$global:notifyIcon.BalloonTipIcon = `$icon `$global:notifyIcon.BalloonTipTitle = `$Title `$global:notifyIcon.BalloonTipText = `$Message `$global:notifyIcon.ShowBalloonTip(30000) } # Set access token if configured $tokenLine `$ntfyExe = "$InstallDir\ntfy.exe" # Subscribe and process JSON output line by line & `$ntfyExe subscribe $topicUrlsString 2>&1 | ForEach-Object { `$line = `$_ if (`$line -match '"event":"message"') { try { `$json = `$line | ConvertFrom-Json `$title = if (`$json.title) { `$json.title } else { `$json.topic } `$message = `$json.message `$priority = if (`$json.priority) { `$json.priority } else { 3 } Show-Notification -Title `$title -Message `$message -Priority `$priority } catch { } } } `$global:notifyIcon.Dispose() "@ $psScriptPath = "$ConfigDir\run-subscribe.ps1" $psScriptContent | Out-File -FilePath $psScriptPath -Encoding UTF8 Write-Host "Notification script saved to: $psScriptPath" -ForegroundColor Green # --- Create VBS wrapper for hidden startup (no console window) --- $vbsContent = @" Set WshShell = CreateObject("WScript.Shell") WshShell.Run "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File ""$psScriptPath""", 0 Set WshShell = Nothing "@ $vbsPath = "$ConfigDir\run-subscribe-hidden.vbs" $vbsContent | Out-File -FilePath $vbsPath -Encoding ASCII Write-Host "Hidden launcher saved to: $vbsPath" -ForegroundColor Green Write-Host "" # --- Create startup shortcut --- Write-Host "Creating startup shortcut..." $startupPath = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup" $shortcutPath = "$startupPath\ntfy-subscribe.lnk" $shell = New-Object -ComObject WScript.Shell $shortcut = $shell.CreateShortcut($shortcutPath) $shortcut.TargetPath = "wscript.exe" $shortcut.Arguments = "`"$vbsPath`"" $shortcut.WorkingDirectory = $ConfigDir $shortcut.WindowStyle = 7 # Minimized $shortcut.Description = "ntfy notification subscriber" $shortcut.Save() Write-Host "Startup shortcut created at: $shortcutPath" -ForegroundColor Green Write-Host "" # --- Start the subscriber now --- Write-Host "Starting ntfy subscriber..." # Stop any existing ntfy or subscriber processes Stop-Process -Name ntfy -ErrorAction SilentlyContinue Get-Process powershell -ErrorAction SilentlyContinue | Where-Object { $_.Id -ne $PID } | ForEach-Object { try { $cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($_.Id)" -ErrorAction SilentlyContinue).CommandLine if ($cmdLine -like "*run-subscribe*") { Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue } } catch {} } Start-Sleep -Seconds 1 $process = Start-Process -FilePath "powershell" ` -ArgumentList @("-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-File", $psScriptPath) ` -WindowStyle Hidden ` -PassThru Start-Sleep -Seconds 2 # --- Print status and management commands --- if ($process -and !$process.HasExited) { Write-Host "" Write-Host "=== Setup Complete ===" -ForegroundColor Green Write-Host "" Write-Host "ntfy is running and will start automatically on login." -ForegroundColor Green Write-Host "You should see Windows toast notifications when messages arrive." Write-Host "" Write-Host "Management commands (run in PowerShell):" -ForegroundColor Cyan Write-Host " Check status: Get-Process ntfy -ErrorAction SilentlyContinue" Write-Host " Stop: Stop-Process -Name ntfy" Write-Host " Start manually: wscript.exe '$vbsPath'" Write-Host " Edit config: notepad '$clientYmlPath'" Write-Host " Edit topics: notepad '$psScriptPath'" Write-Host "" } else { Write-Host "" Write-Host "WARNING: ntfy may not have started correctly." -ForegroundColor Yellow Write-Host "Try running manually: wscript.exe '$vbsPath'" Write-Host "" } Write-Host "To test, send a notification from another machine:" -ForegroundColor Cyan Write-Host " curl -d 'Test message' $ServerUrl/$($Topics[0])" Write-Host ""