Add SandwichReminder-AutoOrder feature for automated sandwich ordering and enhance SandwichReminder logic
This commit is contained in:
@@ -36,11 +36,14 @@ ArnePowershellAutomation/
|
|||||||
│ ├── Config.ps1 # Configuration and state I/O
|
│ ├── Config.ps1 # Configuration and state I/O
|
||||||
│ ├── Elevation.ps1 # UAC elevation helper
|
│ ├── Elevation.ps1 # UAC elevation helper
|
||||||
│ ├── NetworkUtils.ps1 # DNS suffix detection
|
│ ├── NetworkUtils.ps1 # DNS suffix detection
|
||||||
│ └── ToastHelper.ps1 # Windows 11 toast notifications
|
│ ├── ToastHelper.ps1 # Windows 11 toast notifications
|
||||||
|
│ ├── PromptHelper.ps1 # Multi-choice dialog boxes (up to 4 buttons)
|
||||||
|
│ └── SandwichAutoOrder.ps1 # Browser keyboard automation for sandwich auto-order
|
||||||
├── features/ # Automation feature modules
|
├── features/ # Automation feature modules
|
||||||
│ ├── DynamicLock.ps1 # Toggle Dynamic Lock on network presence
|
│ ├── DynamicLock.ps1 # Toggle Dynamic Lock on network presence
|
||||||
│ ├── DefaultBrowser.ps1 # Monitor default browser setting
|
│ ├── DefaultBrowser.ps1 # Monitor default browser setting
|
||||||
│ └── SandwichReminder.ps1 # Time-based reminder notifications
|
│ ├── SandwichReminder.ps1 # Time-based reminder with snooze options
|
||||||
|
│ └── SandwichReminder-AutoOrder.ps1 # Personal browser automation for sandwich ordering
|
||||||
└── data/
|
└── data/
|
||||||
├── config.json # Feature toggles & settings (auto-seeded)
|
├── config.json # Feature toggles & settings (auto-seeded)
|
||||||
├── state/
|
├── state/
|
||||||
@@ -123,14 +126,27 @@ Enables/disables Windows Dynamic Lock based on network connectivity.
|
|||||||
### DefaultBrowser
|
### DefaultBrowser
|
||||||
Monitors if your default browser matches expected ProgId (app identifier).
|
Monitors if your default browser matches expected ProgId (app identifier).
|
||||||
- **Settings**: Target ProgId (e.g., "FirefoxURL", "OperaGXStable")
|
- **Settings**: Target ProgId (e.g., "FirefoxURL", "OperaGXStable")
|
||||||
- **Logic**: On each run, reads current ProgId; if mismatch, shows toast with link to settings; max once per day
|
- **Logic**: On each run, reads current ProgId; if mismatch, shows a Yes/No dialog; "Yes" opens Windows Settings → Default apps for manual selection; max once per day
|
||||||
- **State Tracked**: Last notification date to avoid spam
|
- **State Tracked**: Last notification date to avoid spam
|
||||||
|
- **Note**: Windows 11 protects the default browser registry key with a hash; the browser cannot be changed programmatically, hence the guided Settings approach
|
||||||
|
|
||||||
### SandwichReminder
|
### SandwichReminder
|
||||||
Shows a toast reminder at a specific time on a specific network.
|
Shows a reminder dialog at a specific time on a specific network with snooze options.
|
||||||
- **Settings**: Reminder time (HH:MM), network domain suffix, reminder URL, notification window (±5 minutes)
|
- **Settings**: Reminder time (HH:MM), network domain suffix, reminder URL, notification window in minutes
|
||||||
- **Logic**: If on configured network AND current time is within ±5 min of reminder time AND not shown today, display toast with link
|
- **Logic**: If on configured network AND current time is within window of reminder time AND not shown today, display a 4-choice dialog: Order now / Snooze 15 min / Snooze 1 hour / No
|
||||||
- **State Tracked**: Last notification date
|
- **State Tracked**: Last shown date, snooze-until timestamp
|
||||||
|
- **Auto-order delegation**: If `SandwichReminder-AutoOrder` feature is enabled, selecting "Order now" triggers the automated browser flow instead of just opening the URL
|
||||||
|
|
||||||
|
### SandwichReminder-AutoOrder
|
||||||
|
Personal browser automation feature that drives the sandwich ordering website via keyboard navigation.
|
||||||
|
- **Settings**: Base URL, item ID, browser window title hint, tab counts for each navigation step, delays, calibration mode
|
||||||
|
- **Logic**: Opens the item modal URL, brings the browser window to the foreground (with retry), then sends keyboard inputs to: toggle option checkboxes → click add-to-cart → refresh page → tab to mini-cart → open cart → tab to confirm order button → open delivery popup
|
||||||
|
- **Key settings**:
|
||||||
|
- `tabsToOption1` / `tabsBetweenOptions` / `tabsToAddButton`: navigation within item modal
|
||||||
|
- `refreshBeforeCart`: send F5 after add-to-cart to reset focus to a known page position
|
||||||
|
- `tabsToCartButton` / `tabsToConfirmButton`: navigation after refresh
|
||||||
|
- `calibrationOnly` + `calibrationTabs`: safe mode to manually count tab stops without clicking
|
||||||
|
- **Note**: Uses WScript.Shell SendKeys (best-effort); relies on stable tab order in the target website
|
||||||
|
|
||||||
## Usage Guide
|
## Usage Guide
|
||||||
|
|
||||||
@@ -168,54 +184,6 @@ When you select a feature for configuration, you'll be prompted for each setting
|
|||||||
- **Type: time** → Enter HH:MM format
|
- **Type: time** → Enter HH:MM format
|
||||||
- Press Enter to accept the displayed default
|
- Press Enter to accept the displayed default
|
||||||
|
|
||||||
## Testing Plan
|
|
||||||
|
|
||||||
### T1 — Basic Logging & Config Load
|
|
||||||
- [ ] Run `.\configure.ps1` → Main menu displays
|
|
||||||
- [ ] Verify `internal/data/config.json` was created (if first run)
|
|
||||||
- [ ] Verify `internal/data/logs/automation-<date>.log` has initialization entries
|
|
||||||
|
|
||||||
### T2 — Feature Discovery & Configuration Menu
|
|
||||||
- [ ] Select menu option 4 (Configure)
|
|
||||||
- [ ] Verify all three features appear (DynamicLock, DefaultBrowser, SandwichReminder)
|
|
||||||
- [ ] Toggle DynamicLock (1) → Verify enabled flag flips in config.json
|
|
||||||
- [ ] Select C1 (configure DynamicLock) → Modify network suffix, save, verify in config.json
|
|
||||||
|
|
||||||
### T3 — Registration & Task Scheduler
|
|
||||||
- [ ] Select menu option 2 (Register)
|
|
||||||
- [ ] If prompted for UAC, approve elevation
|
|
||||||
- [ ] Verify success message and `last-register-result.json` shows success=true
|
|
||||||
- [ ] Open Task Scheduler → Navigate to Library root → Verify "PSAutomation-Runner" task exists
|
|
||||||
- [ ] Right-click task → Properties → Verify "Run as current user" (not elevated), AtLogOn trigger, 2-min repetition
|
|
||||||
|
|
||||||
### T4 — Feature Execution (DynamicLock)
|
|
||||||
- [ ] Register the runner (option 2)
|
|
||||||
- [ ] Enable DynamicLock in configuration menu (option 4)
|
|
||||||
- [ ] Configure DynamicLock network to current network suffix (use `Get-DnsClient` or `ipconfig /all` to find)
|
|
||||||
- [ ] Wait for runner to execute (at next 2-min interval or logon event)
|
|
||||||
- [ ] Check `internal/data/logs/automation-<date>.log` for "DynamicLock" entries
|
|
||||||
- [ ] Verify state.json has updated `isConnected` flag
|
|
||||||
|
|
||||||
### T5 — Feature Execution (DefaultBrowser)
|
|
||||||
- [ ] Enable DefaultBrowser in configuration menu
|
|
||||||
- [ ] Verify toast notification appears (if configured ProgId differs from current default)
|
|
||||||
- [ ] Click "Yes" button on toast → Should open Settings → Default apps
|
|
||||||
- [ ] Check logs for "DefaultBrowser" entries and state.json for `lastShownDate`
|
|
||||||
|
|
||||||
### T6 — Feature Execution (SandwichReminder)
|
|
||||||
- [ ] Enable SandwichReminder in configuration menu
|
|
||||||
- [ ] Configure reminderTime to a time 1–2 minutes in the future (e.g., if current time is 14:35, set to 14:37)
|
|
||||||
- [ ] Configure to current network suffix
|
|
||||||
- [ ] Wait for runner to execute
|
|
||||||
- [ ] Verify toast appears with configured URL
|
|
||||||
- [ ] Check logs for "SandwichReminder" entries and state.json for `lastShownDate`
|
|
||||||
|
|
||||||
### T7 — Error Isolation
|
|
||||||
- [ ] Intentionally break one feature (e.g., edit `internal/features/DynamicLock.ps1`, add invalid syntax)
|
|
||||||
- [ ] Wait for runner to execute
|
|
||||||
- [ ] Verify broken feature logged an error in automation-<date>.log
|
|
||||||
- [ ] Verify other features still executed successfully (logged their own entries)
|
|
||||||
|
|
||||||
## Known Limitations
|
## Known Limitations
|
||||||
|
|
||||||
- **Default Browser Registry Protection**: On Windows 11, Microsoft protects the default browser registry key with a hash. The DefaultBrowser feature reads the ProgId but cannot *set* the default browser programmatically. The toast provides a link to Settings for manual override.
|
- **Default Browser Registry Protection**: On Windows 11, Microsoft protects the default browser registry key with a hash. The DefaultBrowser feature reads the ProgId but cannot *set* the default browser programmatically. The toast provides a link to Settings for manual override.
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
$FeatureMeta = @{
|
||||||
|
Name = 'SandwichReminder-AutoOrder'
|
||||||
|
Description = 'Personal automation settings for sandwich auto-order browser flow'
|
||||||
|
Settings = @(
|
||||||
|
@{
|
||||||
|
Key = 'baseUrl'
|
||||||
|
Label = 'Base URL'
|
||||||
|
Type = 'string'
|
||||||
|
Default = 'https://ylos-kitchen.unipage.eu'
|
||||||
|
Description = 'Base URL of the ordering website'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'itemId'
|
||||||
|
Label = 'Item ID'
|
||||||
|
Type = 'string'
|
||||||
|
Default = 'dmwmo3'
|
||||||
|
Description = 'Item ID for direct item modal URL (default = Smoske)'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'windowTitleHint'
|
||||||
|
Label = 'Browser Window Title Hint'
|
||||||
|
Type = 'string'
|
||||||
|
Default = "YLO'S Kitchen"
|
||||||
|
Description = 'Used to bring browser window to foreground before sending keys'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'defaultItemRemark'
|
||||||
|
Label = 'Default Item Remark'
|
||||||
|
Type = 'string'
|
||||||
|
Default = 'zonder tomaat aub'
|
||||||
|
Description = 'For next step (checkout automation)'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'defaultOrderRemark'
|
||||||
|
Label = 'Default Order Remark'
|
||||||
|
Type = 'string'
|
||||||
|
Default = 'levering in Sioen Bistro'
|
||||||
|
Description = 'For next step (checkout automation)'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'initialDelayMs'
|
||||||
|
Label = 'Initial Delay (ms)'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 2500
|
||||||
|
Description = 'Wait time after opening browser before key automation starts'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'activationTimeoutMs'
|
||||||
|
Label = 'Activation Timeout (ms)'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 10000
|
||||||
|
Description = 'How long to retry foregrounding the browser window'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'stepDelayMs'
|
||||||
|
Label = 'Key Step Delay (ms)'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 120
|
||||||
|
Description = 'Delay between keyboard steps'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'optionToggleKey'
|
||||||
|
Label = 'Option Toggle Key'
|
||||||
|
Type = 'string'
|
||||||
|
Default = 'SPACE'
|
||||||
|
Description = 'Key used to toggle option checkbox: SPACE or ENTER'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'calibrationOnly'
|
||||||
|
Label = 'Calibration Only'
|
||||||
|
Type = 'bool'
|
||||||
|
Default = $false
|
||||||
|
Description = 'If enabled, only tabs through the modal and stops (no clicks/add)'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'calibrationTabs'
|
||||||
|
Label = 'Calibration Tabs'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 0
|
||||||
|
Description = 'Number of tabs to send in calibration mode before stopping'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'tabsToOption1'
|
||||||
|
Label = 'Tabs To Option 1'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 7
|
||||||
|
Description = 'Tab count from modal start to first option checkbox'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'tabsBetweenOptions'
|
||||||
|
Label = 'Tabs Between Options'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 2
|
||||||
|
Description = 'Tab count from first checkbox to second checkbox'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'tabsToAddButton'
|
||||||
|
Label = 'Tabs To Add Button'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 6
|
||||||
|
Description = 'Tab count from second checkbox to add-to-cart button'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'openCartPopupAfterAdd'
|
||||||
|
Label = 'Open Cart Popup After Add'
|
||||||
|
Type = 'bool'
|
||||||
|
Default = $true
|
||||||
|
Description = 'Open mini-cart after adding item'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'refreshBeforeCart'
|
||||||
|
Label = 'Refresh Before Cart'
|
||||||
|
Type = 'bool'
|
||||||
|
Default = $true
|
||||||
|
Description = 'Refresh the page after add-to-cart to reset focus to page start before tabbing to mini-cart'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'postRefreshDelayMs'
|
||||||
|
Label = 'Post-Refresh Delay (ms)'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 2500
|
||||||
|
Description = 'Wait time after F5 refresh before tabbing to mini-cart'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'tabsToCartButton'
|
||||||
|
Label = 'Tabs To Cart Button'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 5
|
||||||
|
Description = 'Tab count from page start (after refresh) to mini-cart button'
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
Key = 'tabsToConfirmButton'
|
||||||
|
Label = 'Tabs To Confirm Button'
|
||||||
|
Type = 'int'
|
||||||
|
Default = 3
|
||||||
|
Description = 'Tab count from mini-cart button to confirm order button (opens delivery popup)'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Feature {
|
||||||
|
param(
|
||||||
|
[hashtable]$Config,
|
||||||
|
[hashtable]$State
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not $State) { $State = @{} }
|
||||||
|
return $State
|
||||||
|
}
|
||||||
@@ -118,8 +118,21 @@ function Invoke-Feature {
|
|||||||
|
|
||||||
if ($decision -eq 'order') {
|
if ($decision -eq 'order') {
|
||||||
try {
|
try {
|
||||||
Start-Process $Config['url']
|
$rootConfig = Get-Config
|
||||||
Write-Log -Level Info -Message "Opened sandwich order URL: $($Config['url'])" -Feature 'SandwichReminder'
|
$autoOrderConfig = $null
|
||||||
|
|
||||||
|
if ($rootConfig.features.ContainsKey('SandwichReminder-AutoOrder')) {
|
||||||
|
$autoOrderConfig = $rootConfig.features['SandwichReminder-AutoOrder']
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($autoOrderConfig -and $autoOrderConfig['enabled']) {
|
||||||
|
Write-Log -Level Info -Message 'SandwichReminder-AutoOrder is enabled; running auto-order flow.' -Feature 'SandwichReminder'
|
||||||
|
[void](Invoke-SandwichAutoOrderFlow -AutoOrderConfig $autoOrderConfig -FallbackUrl $Config['url'])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Start-Process $Config['url']
|
||||||
|
Write-Log -Level Info -Message "Opened sandwich order URL: $($Config['url'])" -Feature 'SandwichReminder'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Log -Level Error -Message "Failed to open sandwich order URL '$($Config['url'])': $_" -Feature 'SandwichReminder'
|
Write-Log -Level Error -Message "Failed to open sandwich order URL '$($Config['url'])': $_" -Feature 'SandwichReminder'
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
# 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ $script:InternalRoot = $PSScriptRoot # runner.ps1 lives in internal\
|
|||||||
. (Join-Path $InternalRoot 'lib\NetworkUtils.ps1')
|
. (Join-Path $InternalRoot 'lib\NetworkUtils.ps1')
|
||||||
. (Join-Path $InternalRoot 'lib\ToastHelper.ps1')
|
. (Join-Path $InternalRoot 'lib\ToastHelper.ps1')
|
||||||
. (Join-Path $InternalRoot 'lib\PromptHelper.ps1')
|
. (Join-Path $InternalRoot 'lib\PromptHelper.ps1')
|
||||||
|
. (Join-Path $InternalRoot 'lib\SandwichAutoOrder.ps1')
|
||||||
. (Join-Path $InternalRoot 'lib\Config.ps1')
|
. (Join-Path $InternalRoot 'lib\Config.ps1')
|
||||||
|
|
||||||
Write-Log -Level Info -Message '────── Runner started ──────' -Feature 'Runner'
|
Write-Log -Level Info -Message '────── Runner started ──────' -Feature 'Runner'
|
||||||
|
|||||||
Reference in New Issue
Block a user