Price and stock watching
Waiting for a graphics card or a ticket to get cheaper, watching for an item to come back in stock? Instead of refreshing a tab ten times a day, put a check on cron: the script downloads the page itself, extracts the price and sends a push when something changes.
Watching the price
Section titled “Watching the price”The script downloads the page, extracts the price (here via grep, though a CSS
selector in pup or JSON via jq is often handier) and compares it to a stored
value:
#!/usr/bin/env bash# ~/bin/notifly-price — `notifly-price <url> <target_price>`set -euset -a; source ~/.notifly.env; set +a
URL="${1:?url}"TARGET="${2:-0}" # threshold: push if the price is at or below thisUA="Mozilla/5.0 (compatible; notifly-price/1.0)"STATE="/tmp/notifly-price-$(echo -n "$URL" | md5sum | cut -c1-8).state"PREV=$(cat "$STATE" 2>/dev/null || echo 0)
HTML=$(curl -fsSL --compressed -A "$UA" "$URL")
# Grab the first number from the price tag. Tune the selector to the site:# many shops keep the price in JSON-LD ("price":"12990") — then use jq.PRICE=$(echo "$HTML" | grep -oE '"price":"[0-9]+' | head -1 | grep -oE '[0-9]+')[[ -z "$PRICE" ]] && { echo "Price not found — fix the selector" >&2; exit 1; }
# Did the price drop compared to the previous check?if (( PREV > 0 )) && (( PRICE < PREV )); then notifly-send "📉 Цена упала: $PRICE ₽" "Было $PREV ₽\n$URL" 6fi
# Reached the target threshold?if (( TARGET > 0 )) && (( PRICE <= TARGET )) && (( PREV > TARGET || PREV == 0 )); then notifly-send "🎯 Цена ≤ $TARGET ₽" "Сейчас $PRICE ₽\n$URL" 8fi
echo "$PRICE" > "$STATE"Add it to cron once every two hours (be kind to the site — no more often):
# ~/bin/notifly-price <url> <target>17 */2 * * * ~/bin/notifly-price 'https://shop.example.com/gpu-4080' 89990Stock availability (“back in stock”)
Section titled “Stock availability (“back in stock”)”Sometimes what matters isn’t the price but the fact it appeared. Detect it by a marker in the markup (“Add to cart” instead of “Out of stock”):
#!/usr/bin/env bash# ~/bin/notifly-stock — `notifly-stock <url>`set -euset -a; source ~/.notifly.env; set +a
URL="${1:?url}"STATE="/tmp/notifly-stock-$(echo -n "$URL" | md5sum | cut -c1-8).state"PREV=$(cat "$STATE" 2>/dev/null || echo OUT)
HTML=$(curl -fsSL --compressed -A "Mozilla/5.0 (notifly-stock)" "$URL")
# In-stock marker. Tune the string to the specific shop.if echo "$HTML" | grep -qi 'в корзину\|add to cart'; then NOW=INelse NOW=OUTfi
if [[ "$NOW" == "IN" && "$PREV" == "OUT" ]]; then notifly-send "🛒 Товар в наличии!" "$URL" 8fiecho "$NOW" > "$STATE"Tip: reading the price from JSON with jq is more robust
Section titled “Tip: reading the price from JSON with jq is more robust”Many sites put the price in JSON-LD <script type="application/ld+json">.
That’s more stable than parsing HTML:
PRICE=$(echo "$HTML" \ | grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)' \ | jq -r '..|.price? // empty' | head -1)Windows: PowerShell + Task Scheduler
Section titled “Windows: PowerShell + Task Scheduler”A PowerShell equivalent. Assumes a configured
function Send-Notifly.
param([Parameter(Mandatory)][string]$Url, [int]$Target = 0). $env:USERPROFILE\Documents\WindowsPowerShell\Notifly.ps1
$hash = ([System.BitConverter]::ToString( (New-Object Security.Cryptography.MD5CryptoServiceProvider).ComputeHash( [Text.Encoding]::UTF8.GetBytes($Url))) -replace '-').Substring(0, 8)$State = "$env:LOCALAPPDATA\notifly-price-$hash.txt"$prev = if (Test-Path $State) { [int](Get-Content $State) } else { 0 }
$html = Invoke-WebRequest -Uri $Url -UserAgent "Mozilla/5.0 (notifly-price)" -UseBasicParsing# First number from "price":"12990". Tune the selector to the site.$price = [int]([regex]::Match($html.Content, '"price":"(\d+)').Groups[1].Value)if ($price -eq 0) { throw "Price not found — fix the selector" }
if ($prev -gt 0 -and $price -lt $prev) { Send-Notifly -Title "📉 Цена упала: $price ₽" -Message "Было $prev ₽`n$Url" -Priority 6}if ($Target -gt 0 -and $price -le $Target -and ($prev -gt $Target -or $prev -eq 0)) { Send-Notifly -Title "🎯 Цена ≤ $Target ₽" -Message "Сейчас $price ₽`n$Url" -Priority 8}$price | Set-Content $StateSchedule it once every two hours:
$Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\scripts\Notifly-Price.ps1 -Url 'https://shop.example.com/gpu' -Target 89990"$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) ` -RepetitionInterval (New-TimeSpan -Hours 2)Register-ScheduledTask -TaskName "Notifly Price Watch" -Action $Action -Trigger $TriggerBenefits
Section titled “Benefits”- Catch the discount without living in the browser. Cron checks for you around the clock.
- Threshold + drop. Separately “got cheaper” (priority 6) and “hit the goal” (priority 8, so you definitely don’t sleep through it).
- Not just prices. The same trick works for any change on a page: an application status, a schedule, a free doctor’s appointment slot.
Further improvements
Section titled “Further improvements”- For sites where JavaScript draws everything, read the price with the
built-in browser monitor instead of
curl. - Keep a price history and send a chart of the weekly minimum.
- Attach a product image link to the push via extra fields.