Claude Skills Guide

Building a Chrome Extension for Gaming Deal Finding

Building a Chrome extension for gaming deal discovery combines web scraping, API integration, and real-time price monitoring into a practical developer tool. This guide walks through the architecture, implementation patterns, and key considerations for creating a functional gaming deal finder extension.

Core Architecture

A gaming deal finder extension needs three main components: a background service for periodic price checks, a content script for displaying deals on retailer sites, and a popup interface for quick access to saved deals. The background service handles API calls and stores data, while the popup provides the user-facing interface.

The foundation starts with the manifest file:

{
  "manifest_version": 3,
  "name": "Gaming Deal Finder",
  "version": "1.0.0",
  "permissions": ["storage", "alarms", "activeTab"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": "icon.png"
  }
}

Service workers in Manifest V3 handle background tasks differently than the older event pages. You’ll use the alarms API for scheduling periodic checks:

// background.js
chrome.alarms.create('priceCheck', { periodInMinutes: 30 });

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'priceCheck') {
    checkAllStoredDeals();
  }
});

Price Tracking Implementation

Store your tracked games in Chrome’s synchronized storage. This keeps data consistent across devices when the user signs into Chrome:

// Storage manager for tracked games
class DealStorage {
  static async addGame(gameData) {
    const { games = [] } = await chrome.storage.sync.get('games');
    const existing = games.find(g => g.id === gameData.id);
    
    if (!existing) {
      games.push({
        ...gameData,
        addedAt: Date.now(),
        priceHistory: [{ price: gameData.currentPrice, date: Date.now() }]
      });
      await chrome.storage.sync.set({ games });
    }
  }

  static async getGames() {
    const { games = [] } = await chrome.storage.sync.get('games');
    return games;
  }

  static async updatePrice(gameId, newPrice) {
    const games = await this.getGames();
    const game = games.find(g => g.id === gameId);
    
    if (game) {
      game.priceHistory.push({ price: newPrice, date: Date.now() });
      game.currentPrice = newPrice;
      game.lowestPrice = Math.min(game.lowestPrice || newPrice, newPrice);
      await chrome.storage.sync.set({ games });
    }
  }
}

API Integration Patterns

Most gaming deals come from aggregators like CheapShark, which provides an API for accessing store prices. Here’s how to integrate:

// API client for deal fetching
class DealAPI {
  static async searchGame(title) {
    const response = await fetch(
      `https://www.cheapshark.com/api/1.0/games?title=${encodeURIComponent(title)}&limit=10`
    );
    
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    
    return response.json();
  }

  static async getDealDetails(dealId) {
    const response = await fetch(
      `https://www.cheapshark.com/api/1.0/deals/${dealId}`
    );
    return response.json();
  }

  static async getAllStores() {
    const response = await fetch(
      'https://www.cheapshark.com/api/1.0/stores'
    );
    return response.json();
  }
}

Handle rate limiting gracefully by implementing exponential backoff:

async function fetchWithRetry(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url);
      
      if (response.status === 429) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      
      return response;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
    }
  }
}

Content Script for Deal Display

Content scripts run in the context of web pages and can inject deal information directly into retailer sites:

// content.js - Run on supported retailer pages
const DEAL_WIDGET_STYLE = `
  .deal-finder-widget {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: #1a1a2e;
    border-radius: 8px;
    padding: 16px;
    max-width: 300px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    z-index: 999999;
  }
`;

function injectDealWidget(deals) {
  const style = document.createElement('style');
  style.textContent = DEAL_WIDGET_STYLE;
  document.head.appendChild(style);

  const widget = document.createElement('div');
  widget.className = 'deal-finder-widget';
  widget.innerHTML = `
    <h4>Found ${deals.length} better deals</h4>
    ${deals.slice(0, 3).map(deal => `
      <div class="deal-item">
        <span>${deal.store}</span>
        <strong>$${deal.price}</strong>
        <a href="${deal.url}" target="_blank">View</a>
      </div>
    `).join('')}
  `;
  
  document.body.appendChild(widget);
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'showDeals') {
    injectDealWidget(request.deals);
  }
});

The popup provides quick access without leaving the current page:

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 320px; font-family: system-ui, sans-serif; }
    .header { background: #16213e; color: white; padding: 12px; }
    .game-list { max-height: 400px; overflow-y: auto; }
    .game-item { padding: 12px; border-bottom: 1px solid #eee; }
    .price-original { text-decoration: line-through; color: #666; }
    .price-current { color: #2ecc71; font-weight: bold; }
    .discount { background: #e74c3c; color: white; padding: 2px 6px; border-radius: 4px; }
  </style>
</head>
<body>
  <div class="header">
    <h3>🎮 Tracked Games</h3>
  </div>
  <div class="game-list" id="gameList"></div>
  <script src="popup.js"></script>
</body>
</html>
// popup.js
document.addEventListener('DOMContentLoaded', async () => {
  const games = await DealStorage.getGames();
  const stores = await DealAPI.getAllStores();
  
  const gameList = document.getElementById('gameList');
  
  if (games.length === 0) {
    gameList.innerHTML = '<p style="padding: 20px;">No games tracked yet</p>';
    return;
  }

  gameList.innerHTML = games.map(game => {
    const discount = Math.round((1 - game.currentPrice / game.cheapestPriceEver) * 100);
    return `
      <div class="game-item">
        <h4>${game.title}</h4>
        <p>
          <span class="price-original">$${game.cheapestPriceEver}</span>
          <span class="price-current">$${game.currentPrice}</span>
          <span class="discount">-${discount}%</span>
        </p>
      </div>
    `;
  }).join('');
});

Notifications for Price Drops

Chrome’s notifications API alerts users when prices drop below their threshold:

async function checkPriceAlerts() {
  const games = await DealStorage.getGames();
  const { alerts = {} } = await chrome.storage.sync.get('alerts');
  
  for (const game of games) {
    const alertPrice = alerts[game.id];
    if (alertPrice && game.currentPrice <= alertPrice) {
      chrome.notifications.create({
        type: 'basic',
        iconUrl: 'icon.png',
        title: 'Price Drop Alert!',
        message: `${game.title} is now $${game.currentPrice} (below $${alertPrice})`
      });
    }
  }
}

Deployment Considerations

When publishing to the Chrome Web Store, ensure your extension handles edge cases properly. Test with various store layouts, implement proper error handling for API failures, and provide clear user onboarding. Privacy policies are required for extensions that access sensitive data.

Consider adding support for multiple language regions and currency preferences for international users. The CheapShark API supports multiple store regions, which you can use for a more comprehensive deal-finding experience.

Built by theluckystrike — More at zovo.one