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.png1. 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. Open Chrome and navigate to
chrome://extensions/ - 2. Enable "Developer mode" in the top right
- 3. Click "Load unpacked" and select your extension directory
- 4. The extension should appear in your extensions list
Configure API Key
- 1. Click the Momentize.ai extension icon in your browser toolbar
- 2. Enter your API key in the popup
- 3. Click "Save Settings"
Test Integration
- 1. Go to
chat.openai.comorclaude.ai - 2. Start a conversation with purchase intent: "I need to buy a laptop"
- 3. Sponsored content should appear after the AI's response
- 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. Create a Chrome Web Store developer account ($5 fee)
- 2. Zip your extension files
- 3. Upload to the Chrome Web Store Developer Dashboard
- 4. Fill out store listing details and screenshots
- 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