Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationCore Add-Type -AssemblyName WindowsBase # ------------------ Configurable Settings ------------------ $AccentColor = "#5F8AE8" $flash = $true # Set to $false for no border flash $darkMode = $true # Set to $true or $false only $TitleText = "Company Name - Remote Support" $SubText = "A technician is requesting to connect to your computer.`nDo you approve remote access?" $TimeoutSeconds = 30 $logPath = "$env:LOCALAPPDATA\Gorelo\ConnectConsent.txt" $SaveToGorelo = $true #save to "LastConsent" Gorelo Variable # ------------------ Theme Colors ------------------ if ($darkMode) { $BackgroundColor = "#1E1E1E" $TextColor = "#FFFFFF" $OtherButtonColor = "#3C3C3C" $BorderColor = "#2A2A2A" $ButtonBorderColor = "#505050" $FlashColor = "#FFA500" } else { $BackgroundColor = "#F0F0F0" $TextColor = "#000000" $OtherButtonColor = "#E1E1E1" $BorderColor = "#C0C0C0" $ButtonBorderColor = "#A0A0A0" $FlashColor = "#FC5908" } # Brushes $BackgroundBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($BackgroundColor) $TextBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($TextColor) $OtherButtonBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($OtherButtonColor) $AccentBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($AccentColor) $BorderBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($BorderColor) $ButtonBorderBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($ButtonBorderColor) $FlashBrush = (New-Object Windows.Media.BrushConverter).ConvertFromString($FlashColor) function GetContrastTextBrush($hexColor) { $r = [Convert]::ToInt32($hexColor.Substring(1,2),16) $g = [Convert]::ToInt32($hexColor.Substring(3,2),16) $b = [Convert]::ToInt32($hexColor.Substring(5,2),16) $luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) if ($luminance -lt 128) { return [Windows.Media.Brushes]::White } else { return [Windows.Media.Brushes]::Black } } $AccentTextBrush = GetContrastTextBrush $AccentColor # ------------------ Create Window ------------------ $Window = New-Object Windows.Window $Window.Title = $TitleText $Window.Width = 420 $Window.SizeToContent = "Height" $Window.WindowStartupLocation = "CenterScreen" $Window.ResizeMode = "NoResize" $Window.WindowStyle = "None" $Window.Background = [Windows.Media.Brushes]::Transparent $Window.AllowsTransparency = $true $Window.Topmost = $true $Window.ShowInTaskbar = $false # ------------------ Card ------------------ $Card = New-Object Windows.Controls.Border $Card.CornerRadius = 12 $Card.Padding = 20 $Card.Background = $BackgroundBrush $Card.BorderBrush = $BorderBrush $Card.BorderThickness = 1.5 # ------------------ Layout ------------------ $Layout = New-Object Windows.Controls.StackPanel $Layout.Orientation = "Vertical" $Layout.HorizontalAlignment = "Stretch" $Card.Child = $Layout # ------------------ Header ------------------ $Header = New-Object Windows.Controls.StackPanel $Header.Orientation = "Horizontal" $Header.Margin = "0,0,0,10" $Header.HorizontalAlignment = "Left" $Icon = New-Object Windows.Controls.TextBlock $Icon.Text = [char]0xE7F4 $Icon.FontFamily = "Segoe MDL2 Assets" $Icon.FontSize = 22 $Icon.Margin = "0,0,8,0" $Icon.Foreground = $AccentBrush $TitleBlock = New-Object Windows.Controls.TextBlock $TitleBlock.Text = $TitleText $TitleBlock.FontSize = 18 $TitleBlock.FontWeight = "SemiBold" $TitleBlock.VerticalAlignment = "Center" $TitleBlock.Foreground = $TextBrush $Header.Children.Add($Icon) | Out-Null $Header.Children.Add($TitleBlock) | Out-Null $Layout.Children.Add($Header) | Out-Null # ------------------ Message ------------------ $MessageBlock = New-Object Windows.Controls.TextBlock $MessageBlock.FontSize = 13 $MessageBlock.Text = $SubText $MessageBlock.TextWrapping = "Wrap" $MessageBlock.Width = 360 $MessageBlock.Margin = "0,0,0,12" $MessageBlock.HorizontalAlignment = "Left" $MessageBlock.Foreground = $TextBrush $Layout.Children.Add($MessageBlock) | Out-Null # ------------------ Countdown ------------------ $CountdownBlock = New-Object Windows.Controls.TextBlock $CountdownBlock.FontSize = 12 $CountdownBlock.Margin = "0,0,0,16" $CountdownBlock.HorizontalAlignment = "Left" $CountdownBlock.Foreground = $TextBrush if ($TimeoutSeconds -gt 0) { $Layout.Children.Add($CountdownBlock) | Out-Null } # ------------------ Buttons ------------------ $ButtonPanel = New-Object Windows.Controls.StackPanel $ButtonPanel.Orientation = "Horizontal" $ButtonPanel.HorizontalAlignment = "Right" function CreateRoundedButton($text, $bgBrush, $fgBrush) { $btn = New-Object Windows.Controls.Button $btn.Content = $text $btn.MinWidth = 86 $btn.Height = 36 $btn.Padding = "12,6" $btn.Margin = "6,0,0,0" $btn.Background = $bgBrush $btn.Foreground = $fgBrush $btn.BorderBrush = $ButtonBorderBrush $btn.BorderThickness = 1 $template = @" "@ $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($template)) $btn.Template = [Windows.Markup.XamlReader]::Load($reader) return $btn } $btnYes = CreateRoundedButton "Yes" $AccentBrush $AccentTextBrush $btnNo = CreateRoundedButton "No" $OtherButtonBrush $TextBrush $btnLater = CreateRoundedButton "Later" $OtherButtonBrush $TextBrush $ButtonPanel.Children.Add($btnYes) | Out-Null $ButtonPanel.Children.Add($btnNo) | Out-Null $ButtonPanel.Children.Add($btnLater) | Out-Null $Layout.Children.Add($ButtonPanel) | Out-Null # ------------------ Interactivity ------------------ $Card.Add_MouseLeftButtonDown({ $Window.DragMove() }) | Out-Null $script:Choice = $null $stopTimer = { if ($script:timer -and $script:timer.IsEnabled) { $script:timer.Stop() } } $btnYes.Add_Click( { & $stopTimer; $script:Choice = 'Yes'; $Window.Close() }) | Out-Null $btnNo.Add_Click( { & $stopTimer; $script:Choice = 'No'; $Window.Close() }) | Out-Null $btnLater.Add_Click( { & $stopTimer; $script:Choice = 'Later'; $Window.Close() }) | Out-Null # ------------------ Timeout Logic ------------------ if ($TimeoutSeconds -gt 0) { $script:remaining = [int]$TimeoutSeconds function UpdateCountdown { $CountdownBlock.Text = "Auto-cancels in {0:D2}:{1:D2}" -f ([int]($script:remaining / 60)), ([int]($script:remaining % 60)) } UpdateCountdown $script:timer = New-Object Windows.Threading.DispatcherTimer $script:timer.Interval = [TimeSpan]::FromSeconds(1) $script:timer.Add_Tick({ $script:remaining-- if ($script:remaining -le 0) { $script:timer.Stop() if (-not $script:Choice) { $script:Choice = 'TimedOut' $Window.Close() } } else { UpdateCountdown } }) | Out-Null $script:timer.Start() } # ------------------ Border Flash ------------------ if ($flash) { $Card.BorderBrush = $FlashBrush $flashCount = 0 $maxFlashes = 9 # Create the timer $flashTimer = New-Object Windows.Threading.DispatcherTimer $flashTimer.Interval = [TimeSpan]::FromMilliseconds(80) # Define the tick handler as a script block $tickHandler = { if ($Card.BorderBrush -eq $FlashBrush) { $Card.BorderBrush = $BorderBrush } else { $Card.BorderBrush = $FlashBrush } $script:flashCount++ if ($script:flashCount -ge $maxFlashes) { $flashTimer.Stop() $Card.BorderBrush = $BorderBrush # Reset to default } } # Attach the tick handler $flashTimer.Add_Tick($tickHandler) $flashTimer.Start() } # ------------------ Sound Alert ------------------ $Window.Add_Loaded({ try { [System.Media.SystemSounds]::Exclamation.Play() } catch { # Ignore sound errors } }) | Out-Null # ------------------ Show + Output ------------------ $Window.Content = $Card $null = $Window.ShowDialog() if (-not $script:Choice) { $script:Choice = 'Later' } Write-Output "" "$script:Choice" Write-Output "" # ------------------ Log Consent ------------------ $timestamp = Get-Date -Format "yyyyMMdd-HH:mm" $logLine = "$script:Choice - $timestamp" # Ensure directory exists $logDir = [System.IO.Path]::GetDirectoryName($logPath) if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } # Append to file (create if missing) Add-Content -Path $logPath -Value $logLine if ($SaveToGorelo) { # Set Gorelo custom field GoreloAction -SetCustomField -Name 'asset.LastConsent' -Value "$logLine" }