Chrome Extension Guide

Build a Chrome extension that adds sponsored content to ChatGPT, Claude, and other AI chat interfaces.

What We're Building

Extension Features

  • • Automatically detects moments in ChatGPT/Claude conversations
  • • Displays relevant sponsored content blocks after AI responses
  • • Tracks impressions and clicks for analytics
  • • Settings popup for API key configuration
  • • Works across multiple AI chat platforms

Project Structure

momentize-extension/
├── manifest.json          # Extension configuration
├── content.js            # Main integration logic
├── content.css           # Styling for sponsored content
├── popup.html            # Settings popup interface
├── popup.js              # Popup functionality
├── background.js         # Service worker (optional)
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

1. Extension Manifest

Create manifest.json to define extension permissions and structure:

manifest.json
{
  "manifest_version": 3,
  "name": "Momentize.ai - ChatGPT Monetization",
  "version": "1.0.0",
  "description": "Monetize ChatGPT conversations with relevant sponsored content",
  
  "permissions": [
    "activeTab",
    "storage"
  ],
  
  "content_scripts": [{
    "matches": ["https://chat.openai.com/*", "https://claude.ai/*"],
    "js": ["content.js"],
    "css": ["content.css"]
  }],
  
  "background": {
    "service_worker": "background.js"
  },
  
  "action": {
    "default_popup": "popup.html",
    "default_title": "Momentize.ai Settings"
  }
}

2. Content Script Integration

The content script monitors conversations and integrates Momentize.ai:

content.js
// content.js - Inject Momentize.ai into ChatGPT
class MomentizeIntegration {
  constructor() {
    this.apiKey = null;
    this.sessionId = this.generateSessionId();
    this.conversationHistory = [];
    this.init();
  }

  async init() {
    // Get API key from extension storage
    const result = await chrome.storage.sync.get(['apiKey']);
    this.apiKey = result.apiKey;
    
    if (!this.apiKey) {
      console.log('Momentize.ai: No API key configured');
      return;
    }

    this.observeConversation();
  }

  observeConversation() {
    // Watch for new messages in ChatGPT interface
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          this.checkForNewMessages();
        }
      });
    });

    // Observe the conversation container
    const conversationContainer = document.querySelector('[data-testid="conversation-turn"]')?.parentElement;
    if (conversationContainer) {
      observer.observe(conversationContainer, {
        childList: true,
        subtree: true
      });
    }
  }

  async checkForNewMessages() {
    const messages = this.extractConversation();
    
    if (messages.length > this.conversationHistory.length) {
      this.conversationHistory = messages;
      
      // Only analyze after user messages (not every assistant response)
      const lastMessage = messages[messages.length - 1];
      if (lastMessage.role === 'assistant' && messages.length >= 2) {
        await this.analyzeConversation();
      }
    }
  }

  extractConversation() {
    const messages = [];
    const messageElements = document.querySelectorAll('[data-message-author-role]');
    
    messageElements.forEach(element => {
      const role = element.getAttribute('data-message-author-role');
      const content = element.querySelector('.markdown')?.textContent || 
                     element.textContent.trim();
      
      if (content) {
        messages.push({ role, content });
      }
    });
    
    return messages;
  }

  async analyzeConversation() {
    try {
      const response = await fetch('http://localhost:8000/v1/analyze', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiKey}`
        },
        body: JSON.stringify({
          conversation: this.conversationHistory,
          session_id: this.sessionId
        })
      });

      const data = await response.json();
      
      if (data.sponsored_content?.length > 0) {
        this.displaySponsoredContent(data.sponsored_content);
      }
    } catch (error) {
      console.error('Momentize.ai analysis error:', error);
    }
  }

  displaySponsoredContent(ads) {
    // Remove any existing sponsored content
    document.querySelectorAll('.momentize-ad').forEach(el => el.remove());
    
    ads.forEach(ad => {
      const adElement = this.createAdElement(ad);
      this.insertAdIntoConversation(adElement);
      
      // Track impression
      this.trackEvent('impression', ad.impression_id, ad.advertiser_id);
    });
  }

  createAdElement(ad) {
    const adContainer = document.createElement('div');
    adContainer.className = 'momentize-ad';
    adContainer.innerHTML = `
      <div class="momentize-sponsored-block">
        <div class="momentize-disclosure">${ad.disclosure}</div>
        <div class="momentize-content">
          <h3 class="momentize-title">${ad.title}</h3>
          <p class="momentize-description">${ad.description}</p>
          <a href="${ad.cta_url}" target="_blank" class="momentize-cta" 
             data-impression-id="${ad.impression_id}"
             data-advertiser-id="${ad.advertiser_id}">
            ${ad.cta_text}
          </a>
        </div>
      </div>
    `;

    // Add click event listener
    adContainer.querySelector('.momentize-cta').addEventListener('click', (e) => {
      this.trackEvent('click', ad.impression_id, ad.advertiser_id);
    });

    return adContainer;
  }

  insertAdIntoConversation(adElement) {
    // Insert after the last assistant message
    const messages = document.querySelectorAll('[data-message-author-role]');
    const lastMessage = messages[messages.length - 1];
    
    if (lastMessage) {
      lastMessage.parentNode.insertBefore(adElement, lastMessage.nextSibling);
    }
  }

  async trackEvent(eventType, impressionId, advertiserId) {
    try {
      await fetch('http://localhost:8000/v1/events', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiKey}`
        },
        body: JSON.stringify({
          event_type: eventType,
          session_id: this.sessionId,
          impression_id: impressionId,
          advertiser_id: advertiserId,
          metadata: {
            url: window.location.href,
            user_agent: navigator.userAgent
          }
        })
      });
    } catch (error) {
      console.error('Momentize.ai event tracking error:', error);
    }
  }

  generateSessionId() {
    return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  }
}

// Initialize when page loads
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => new MomentizeIntegration());
} else {
  new MomentizeIntegration();
}

3. Sponsored Content Styling

Style the sponsored content blocks to match the platform's design:

content.css
/* content.css - Styling for sponsored content blocks */
.momentize-ad {
  margin: 16px 0;
  border-left: 4px solid #3b82f6;
  background-color: #f8fafc;
  border-radius: 8px;
  overflow: hidden;
}

.momentize-sponsored-block {
  padding: 16px;
}

.momentize-disclosure {
  font-size: 12px;
  color: #6b7280;
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: 8px;
  letter-spacing: 0.5px;
}

.momentize-title {
  font-size: 16px;
  font-weight: 600;
  color: #1f2937;
  margin: 0 0 8px 0;
}

.momentize-description {
  font-size: 14px;
  color: #4b5563;
  margin: 0 0 12px 0;
  line-height: 1.4;
}

.momentize-cta {
  display: inline-block;
  background-color: #3b82f6;
  color: white;
  padding: 8px 16px;
  border-radius: 6px;
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  transition: background-color 0.2s;
}

.momentize-cta:hover {
  background-color: #2563eb;
  text-decoration: none;
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
  .momentize-sponsored-block {
    background-color: #1f2937;
  }
  
  .momentize-title {
    color: #f9fafb;
  }
  
  .momentize-description {
    color: #d1d5db;
  }
}

4. Settings Popup

Create a popup for users to configure their Momentize.ai API key:

popup.html
<!-- popup.html - Extension settings popup -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      width: 300px;
      padding: 20px;
      font-family: system-ui, sans-serif;
    }
    
    .header {
      text-align: center;
      margin-bottom: 20px;
    }
    
    .logo {
      width: 32px;
      height: 32px;
      background: #3b82f6;
      border-radius: 8px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-weight: bold;
      margin-bottom: 8px;
    }
    
    input[type="text"] {
      width: 100%;
      padding: 8px 12px;
      border: 1px solid #d1d5db;
      border-radius: 6px;
      font-size: 14px;
    }
    
    button {
      width: 100%;
      padding: 8px 12px;
      background: #3b82f6;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      cursor: pointer;
      margin-top: 8px;
    }
    
    button:hover {
      background: #2563eb;
    }
    
    .status {
      margin-top: 12px;
      padding: 8px;
      border-radius: 4px;
      font-size: 12px;
      text-align: center;
    }
    
    .status.success {
      background: #dcfce7;
      color: #166534;
    }
    
    .status.error {
      background: #fee2e2;
      color: #dc2626;
    }
  </style>
</head>
<body>
  <div class="header">
    <div class="logo">AI</div>
    <h3>Momentize.ai</h3>
  </div>
  
  <form id="settingsForm">
    <label for="apiKey">API Key:</label>
    <input type="text" id="apiKey" placeholder="Enter your Momentize.ai API key" required>
    <button type="submit">Save Settings</button>
  </form>
  
  <div id="status" class="status" style="display: none;"></div>
  
  <script src="popup.js"></script>
</body>
</html>

5. Popup Functionality

popup.js
// popup.js - Extension popup functionality
document.addEventListener('DOMContentLoaded', async () => {
  const form = document.getElementById('settingsForm');
  const apiKeyInput = document.getElementById('apiKey');
  const statusDiv = document.getElementById('status');

  // Load existing API key
  const result = await chrome.storage.sync.get(['apiKey']);
  if (result.apiKey) {
    apiKeyInput.value = result.apiKey;
  }

  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const apiKey = apiKeyInput.value.trim();
    
    if (!apiKey) {
      showStatus('Please enter an API key', 'error');
      return;
    }

    // Test the API key
    try {
      const response = await fetch('http://localhost:8000/health', {
        headers: {
          'Authorization': `Bearer ${apiKey}`
        }
      });

      if (response.ok) {
        // Save the API key
        await chrome.storage.sync.set({ apiKey });
        showStatus('API key saved successfully!', 'success');
      } else {
        showStatus('Invalid API key', 'error');
      }
    } catch (error) {
      showStatus('Could not connect to Momentize.ai API', 'error');
    }
  });

  function showStatus(message, type) {
    statusDiv.textContent = message;
    statusDiv.className = `status ${type}`;
    statusDiv.style.display = 'block';
    
    setTimeout(() => {
      statusDiv.style.display = 'none';
    }, 3000);
  }
});

Installation & Testing

Load the Extension

  1. 1. Open Chrome and navigate to chrome://extensions/
  2. 2. Enable "Developer mode" in the top right
  3. 3. Click "Load unpacked" and select your extension directory
  4. 4. The extension should appear in your extensions list

Configure API Key

  1. 1. Click the Momentize.ai extension icon in your browser toolbar
  2. 2. Enter your API key in the popup
  3. 3. Click "Save Settings"

Test Integration

  1. 1. Go to chat.openai.com or claude.ai
  2. 2. Start a conversation with purchase intent: "I need to buy a laptop"
  3. 3. Sponsored content should appear after the AI's response
  4. 4. Check the browser console for any error messages

Advanced Features

Multi-Platform Support

Extend the extension to work with other AI platforms by updating the content script selectors:

Platform-Specific Selectors

  • ChatGPT: [data-message-author-role]
  • Claude: .message
  • Bard: .conversation-turn
  • Custom: Add your own selectors

User Preferences

Add options to let users customize their experience:

  • • Enable/disable sponsored content
  • • Set maximum ads per conversation
  • • Choose ad categories
  • • Customize ad appearance

Publishing to Chrome Web Store

  1. 1. Create a Chrome Web Store developer account ($5 fee)
  2. 2. Zip your extension files
  3. 3. Upload to the Chrome Web Store Developer Dashboard
  4. 4. Fill out store listing details and screenshots
  5. 5. Submit for review (typically takes 2-3 days)

Store Guidelines

  • • Clearly describe the extension's functionality
  • • Include privacy policy if collecting user data
  • • Follow Chrome Web Store content policies
  • • Test thoroughly before submission