diff --git a/config.py b/config.py index 7581fb2..6256b62 100644 --- a/config.py +++ b/config.py @@ -5,13 +5,60 @@ Update DISCORD_WEBHOOK_URL with your actual webhook URL # Discord webhook URL - GET THIS FROM YOUR DISCORD SERVER # Server Settings -> Integrations -> Webhooks -> New Webhook -> Copy Webhook URL -DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1471927173353963570/8Xh4Y_D8zRDi5MAP6ZlX0rf2Fc5wEtRKCup74kBZltk2qaatxArFp-yrk7ZWh5EOZbHp" +# This is the default/fallback webhook for anything not specifically routed below +DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1488982276455796756/4HEzQDBF-Hs4a9Iyd7yvj7-SfP0BwrbQGdGBmQ9TL9urWzkTRaHelkvrqX4v9NTf2ZJ_" + +# ============================================================================= +# MULTI-CHANNEL WEBHOOK ROUTING (Optional) +# ============================================================================= +# Route notifications to different Discord channels based on site and/or alert type. +# Leave webhooks as None to use the default DISCORD_WEBHOOK_URL above. +# +# Create webhooks in Discord: Server Settings -> Integrations -> Webhooks -> New Webhook +# +# Lookup priority: +# 1. overrides (specific site + type combo) +# 2. sites (all alerts for a specific store) +# 3. types (all alerts of a specific type) +# 4. default (DISCORD_WEBHOOK_URL) + +DISCORD_WEBHOOKS = { + # Per-site channels - all alerts from a specific store go here first + "sites": { + "target": "https://discord.com/api/webhooks/1488982720901025852/O0KkVFHrQ6-k6FmCqQE4QpuWF6YFJ8YyFgojBPKcQu9g0UHzTpnAU9G1gbhOftARS11R", + "bestbuy": "https://discord.com/api/webhooks/1488982786781216798/AtG6x7FWzJjOsmosq9oPMuwlPrSwNHZJJTUuB3AApjyT7LIOxmEcYeiMFP7HiA_Ov1pn", + "gamestop": "https://discord.com/api/webhooks/1488982854036619355/NWZtydrKEhj-kxKbFi78f1Gpf3h16W_1kAJEcSuIgtjJUoAO8Sk7inOI2T8fb9GXo9xY", + "walmart": "https://discord.com/api/webhooks/1488982929450340375/0g16tJXw8f9UtUrRrLd2jDPWWD__LTDYO_X2VT2WL4F5GBYB8ykVxrjV-bCjzKKjSA5C", + "pokemoncenter": "https://discord.com/api/webhooks/1488983050090975364/DwY9QQvBZa6V-zz8mIg302sukbISSbkXOhDwe1Q6yDG0zAwvaGQcn8iTzNB4D676e3cA", + }, + + # Per-type channels - used when no site-specific webhook is set + "types": { + "restock": "https://discord.com/api/webhooks/1488982403048542269/s1J_p8R6IMGQGypPh_ZYTHHBD0cMnd_BjLGggVWaL2OcyDQAR42KS-CyuHcVr5s21vl3", + "new_drop": "https://discord.com/api/webhooks/1488982511613771898/RLDoHxNjsAx0PfHfT0kZe6-IW3g9PR_ckX_sI-CALcv4Lt0USslvI0rqePqW9gVP7D7v", + "price_change": "https://discord.com/api/webhooks/1488982573723025633/J864UL4vk_q9Rmj4i4wtkGfzB3nKAOc0y7jzBFDgUbhwg4XKm1Z7sWduqRc6x_ThWetc", + "error": "https://discord.com/api/webhooks/1488983128641900657/RP5FRSWwbVaRAf7RdXPs66ETbY8ziI1ekIx7lrHqxp8n200KpzUiX_EylCxXBH5nJD3B", + "captcha": "https://discord.com/api/webhooks/1488983128641900657/RP5FRSWwbVaRAf7RdXPs66ETbY8ziI1ekIx7lrHqxp8n200KpzUiX_EylCxXBH5nJD3B", + }, + + # Override specific site + type combinations (highest priority) + # Use tuples: ("site", "type"): "webhook_url" + "overrides": { + # ("bestbuy", "restock"): "https://discord.com/api/webhooks/xxx/bestbuy-restocks", + # ("pokemoncenter", "new_drop"): "https://discord.com/api/webhooks/xxx/pokemon-drops", + }, +} # Enable/disable Discord notifications NOTIFICATIONS_ENABLED = True # Set to False to disable all Discord alerts NEW_DROP_NOTIFICATIONS = True # Set to False to silence new drop notifications +PRICE_CHANGE_NOTIFICATIONS = True # Set to False to silence price change notifications SKIP_DUPLICATE_SKUS = True # Skip notifications for products we've already notified about +# Restock notification cooldown (prevents duplicate alerts for same product) +# Set to number of hours before a product can trigger another restock notification +RESTOCK_COOLDOWN_HOURS = 6 # Minimum hours between restock alerts for same product + # Discord Bot Token (for interactive bot commands) # Create a bot at https://discord.com/developers/applications # Required for !setlocation, !stores, !stock commands @@ -36,7 +83,7 @@ SORT_BY_NEWEST = True # Which sites to monitor # Note: PokemonCenter has strong bot protection (Imperva) - may need manual workarounds # Note: Walmart has aggressive bot protection (PerimeterX) - may need stealth mode -SITES_ENABLED = ["target", "gamestop", "bestbuy"] # Options: "pokemoncenter", "target", "gamestop", "bestbuy" (disabled - needs rework), "walmart" +SITES_ENABLED = ["target", "bestbuy", "gamestop"] # Options: "pokemoncenter", "target", "gamestop", "bestbuy" (disabled - needs rework), "walmart" # PokemonCenter URLs to monitor POKEMON_CENTER_URLS = [ diff --git a/dashboard/static/app.js b/dashboard/static/app.js index 536ea98..617ac3e 100644 --- a/dashboard/static/app.js +++ b/dashboard/static/app.js @@ -784,6 +784,9 @@ async function loadControlPanel() { // Render scrapers grid renderScrapersGrid(state.scrapers); + // Load Chrome Extension status + loadExtensionStatus(); + // Setup event listeners for control panel buttons setupControlPanelListeners(); @@ -796,10 +799,36 @@ async function loadControlPanel() { updateMonitorStatus(newState); renderScrapersGrid(newState.scrapers); } + loadExtensionStatus(); } }, 5000); } +async function loadExtensionStatus() { + const badge = document.getElementById('extensionBadge'); + const productCount = document.getElementById('extProductCount'); + const skuCount = document.getElementById('extSkuCount'); + const lastSync = document.getElementById('extLastSync'); + + const stats = await api('/extension/stats'); + + if (stats && stats.last_sync) { + // Extension has synced data + badge.textContent = 'Connected'; + badge.className = 'extension-badge connected'; + productCount.textContent = stats.total_products || 0; + skuCount.textContent = stats.total_skus || 0; + lastSync.textContent = formatTime(new Date(stats.last_sync)); + } else { + // No sync yet + badge.textContent = 'No Data'; + badge.className = 'extension-badge disconnected'; + productCount.textContent = '-'; + skuCount.textContent = '-'; + lastSync.textContent = 'Never'; + } +} + function updateMonitorStatus(state) { const badge = document.getElementById('monitorBadge'); const statusText = document.getElementById('monitorStatusText'); diff --git a/dashboard/static/style.css b/dashboard/static/style.css index 4084576..5a690f7 100644 --- a/dashboard/static/style.css +++ b/dashboard/static/style.css @@ -1103,6 +1103,57 @@ h3 { grid-column: span 2; } +/* Chrome Extension Card */ +.extension-card { + grid-column: span 2; + border-color: #ffcb05; +} + +.extension-badge { + padding: 4px 12px; + border-radius: 12px; + font-size: 12px; + font-weight: 600; +} + +.extension-badge.connected { + background: rgba(0, 255, 0, 0.2); + color: #00ff00; +} + +.extension-badge.disconnected { + background: rgba(255, 100, 100, 0.2); + color: #ff6464; +} + +.extension-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 15px; + margin-top: 10px; +} + +.extension-stat { + background: var(--bg-primary); + padding: 12px; + border-radius: 8px; + text-align: center; +} + +.extension-stat .stat-label { + display: block; + font-size: 12px; + color: var(--text-secondary); + margin-bottom: 5px; +} + +.extension-stat .stat-value { + display: block; + font-size: 18px; + font-weight: 600; + color: var(--text-primary); +} + .scrapers-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); diff --git a/dashboard/templates/index.html b/dashboard/templates/index.html index 2947d87..94f4f88 100644 --- a/dashboard/templates/index.html +++ b/dashboard/templates/index.html @@ -274,6 +274,37 @@ + +
Pokemon Center is monitored via the Chrome Extension, not a Python scraper.
+
+ Open Extension |
+ Install from chrome-extension/ folder
+