# SandwichAutoOrder.ps1 — best-effort browser keyboard automation for personal sandwich flow. function Invoke-AppActivateBestEffort { param( [Parameter(Mandatory)]$Shell, [Parameter(Mandatory)][string[]]$TitleCandidates, [int]$TimeoutMs = 10000, [int]$RetryDelayMs = 300 ) $deadline = (Get-Date).AddMilliseconds($TimeoutMs) $candidates = @($TitleCandidates | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) do { foreach ($candidate in $candidates) { if ($Shell.AppActivate($candidate)) { return $true } } Start-Sleep -Milliseconds $RetryDelayMs } while ((Get-Date) -lt $deadline) return $false } function Invoke-SandwichAutoOrderFlow { [CmdletBinding()] param( [Parameter(Mandatory)][hashtable]$AutoOrderConfig, [Parameter(Mandatory)][string]$FallbackUrl, [string]$FeatureName = 'SandwichReminder-AutoOrder' ) $baseUrl = [string]$AutoOrderConfig['baseUrl'] if ([string]::IsNullOrWhiteSpace($baseUrl)) { $baseUrl = 'https://ylos-kitchen.unipage.eu' } $itemId = [string]$AutoOrderConfig['itemId'] if ([string]::IsNullOrWhiteSpace($itemId)) { $itemId = 'dmwmo3' } $windowTitleHint = [string]$AutoOrderConfig['windowTitleHint'] if ([string]::IsNullOrWhiteSpace($windowTitleHint)) { $windowTitleHint = "YLO'S Kitchen" } $initialDelayMs = [int]$AutoOrderConfig['initialDelayMs'] if ($initialDelayMs -le 0) { $initialDelayMs = 2500 } $stepDelayMs = [int]$AutoOrderConfig['stepDelayMs'] if ($stepDelayMs -le 0) { $stepDelayMs = 120 } $toggleKeySetting = [string]$AutoOrderConfig['optionToggleKey'] if ([string]::IsNullOrWhiteSpace($toggleKeySetting)) { $toggleKeySetting = 'SPACE' } $toggleKey = if ($toggleKeySetting.ToUpperInvariant() -eq 'ENTER') { '{ENTER}' } else { ' ' } $calibrationOnly = [bool]$AutoOrderConfig['calibrationOnly'] $calibrationTabs = [int]$AutoOrderConfig['calibrationTabs'] if ($calibrationTabs -lt 0) { $calibrationTabs = 0 } $tabsToOption1 = [int]$AutoOrderConfig['tabsToOption1'] if ($tabsToOption1 -lt 0) { $tabsToOption1 = 3 } $tabsBetweenOptions = [int]$AutoOrderConfig['tabsBetweenOptions'] if ($tabsBetweenOptions -lt 0) { $tabsBetweenOptions = 2 } $tabsToAddButton = [int]$AutoOrderConfig['tabsToAddButton'] if ($tabsToAddButton -lt 0) { $tabsToAddButton = 4 } $openCartPopup = [bool]$AutoOrderConfig['openCartPopupAfterAdd'] $refreshBeforeCart = [bool]$AutoOrderConfig['refreshBeforeCart'] $postRefreshDelayMs = [int]$AutoOrderConfig['postRefreshDelayMs'] if ($postRefreshDelayMs -le 0) { $postRefreshDelayMs = 2500 } $tabsToCartButton = [int]$AutoOrderConfig['tabsToCartButton'] if ($tabsToCartButton -le 0) { $tabsToCartButton = 5 } $tabsToConfirmButton = [int]$AutoOrderConfig['tabsToConfirmButton'] if ($tabsToConfirmButton -le 0) { $tabsToConfirmButton = 3 } $activationTimeoutMs = [int]$AutoOrderConfig['activationTimeoutMs'] if ($activationTimeoutMs -le 0) { $activationTimeoutMs = 10000 } $itemUrl = ($baseUrl.TrimEnd('/') + '/item/' + $itemId) try { Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue | Out-Null Start-Process $itemUrl Write-Log -Level Info -Message "Opened item modal URL for auto-order: $itemUrl" -Feature $FeatureName Start-Sleep -Milliseconds $initialDelayMs $shell = New-Object -ComObject WScript.Shell $hostHint = $null try { $hostHint = ([uri]$baseUrl).Host } catch {} $activated = Invoke-AppActivateBestEffort -Shell $shell -TitleCandidates @( 'Bestel online bij YLO''S Kitchen Pittem', 'Bestel online bij YLO', 'Bestel online bij', $windowTitleHint, ($windowTitleHint -replace "'", ''), $hostHint, 'ylos-kitchen', 'unipage' ) -TimeoutMs $activationTimeoutMs -RetryDelayMs 300 if (-not $activated) { Write-Log -Level Warn -Message "Could not foreground browser window (title hint '$windowTitleHint'). Skipping key automation to avoid acting on the wrong window." -Feature $FeatureName return $false } if ($calibrationOnly) { for ($i = 0; $i -lt $calibrationTabs; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } Write-Log -Level Info -Message ("Calibration mode completed: sent {0} TAB keys and stopped." -f $calibrationTabs) -Feature $FeatureName return $true } # Modal flow keyboard navigation (best-effort; tab indexes are configurable). for ($i = 0; $i -lt $tabsToOption1; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } $shell.SendKeys($toggleKey) Start-Sleep -Milliseconds $stepDelayMs for ($i = 0; $i -lt $tabsBetweenOptions; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } $shell.SendKeys($toggleKey) Start-Sleep -Milliseconds $stepDelayMs for ($i = 0; $i -lt $tabsToAddButton; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } $shell.SendKeys('{ENTER}') Start-Sleep -Milliseconds 350 if ($openCartPopup) { if ($refreshBeforeCart) { $shell.SendKeys('{F5}') Write-Log -Level Info -Message 'Refreshing page to reset focus before tabbing to mini-cart.' -Feature $FeatureName Start-Sleep -Milliseconds $postRefreshDelayMs $activated = Invoke-AppActivateBestEffort -Shell $shell -TitleCandidates @( 'Bestel online bij YLO''S Kitchen Pittem', 'Bestel online bij YLO', 'Bestel online bij', $windowTitleHint, ($windowTitleHint -replace "'", ''), $hostHint, 'ylos-kitchen', 'unipage' ) -TimeoutMs $activationTimeoutMs -RetryDelayMs 300 if (-not $activated) { Write-Log -Level Warn -Message 'Could not re-foreground browser after refresh. Skipping cart popup.' -Feature $FeatureName return $false } } for ($i = 0; $i -lt $tabsToCartButton; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } $shell.SendKeys('{ENTER}') Start-Sleep -Milliseconds $stepDelayMs for ($i = 0; $i -lt $tabsToConfirmButton; $i++) { $shell.SendKeys('{TAB}') Start-Sleep -Milliseconds $stepDelayMs } $shell.SendKeys('{ENTER}') } Write-Log -Level Info -Message 'Auto-order flow executed up to add-to-cart and cart popup open (best-effort).' -Feature $FeatureName return $true } catch { Write-Log -Level Error -Message "Auto-order flow failed: $_" -Feature $FeatureName return $false } }