This guide explains how to add support for new social media platforms to the TapReply extension using the adapter pattern.
The extension uses a Platform Adapter Pattern to abstract platform-specific content extraction. This makes it easy to add new platforms without modifying the core logic.
- PlatformAdapter (Base class) - Abstract interface for all platform adapters
- Platform-specific adapters - Concrete implementations for each platform
- Factory function -
getPlatformAdapter(url)- Returns the appropriate adapter based on URL - Content script - Uses the adapter to extract content and metadata
- Background script - Uses metadata to generate better prompts
The extracted data follows this structure:
{
content: "The actual post content text",
platform: "twitter|reddit|etc",
metadata: {
// Platform-specific metadata fields
author: "username",
postType: "text|image|video|etc",
engagement: { /* engagement metrics */ },
// ... other platform-specific fields
}
}The content and platform fields are common across all platforms and are extracted at the adapter level, while metadata contains platform-specific information.
Create a new class that extends PlatformAdapter in scripts/platform-adapters.js:
class InstagramAdapter extends PlatformAdapter {
constructor() {
super();
this.platform = 'instagram';
}
extractContent() {
// Implement Instagram-specific content extraction
const selectors = [
'[data-testid="post-caption"]',
'.caption',
'.post-content'
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element && element.textContent.trim()) {
return element.textContent.trim();
}
}
return null;
}
extractMetadata() {
return {
author: this.extractAuthor(),
postType: this.extractPostType(),
engagement: this.extractEngagement(),
hashtags: this.extractHashtags()
};
}
extractAuthor() {
const authorElement = document.querySelector('[data-testid="post-author"]');
return authorElement ? authorElement.textContent.trim() : null;
}
extractPostType() {
if (document.querySelector('.video-post')) return 'video';
if (document.querySelector('.carousel-post')) return 'carousel';
return 'image';
}
extractEngagement() {
const engagement = {};
const likesElement = document.querySelector('[data-testid="likes-count"]');
if (likesElement) {
engagement.likes = likesElement.textContent.trim();
}
const commentsElement = document.querySelector('[data-testid="comments-count"]');
if (commentsElement) {
engagement.comments = commentsElement.textContent.trim();
}
return Object.keys(engagement).length > 0 ? engagement : null;
}
extractHashtags() {
const hashtagElements = document.querySelectorAll('a[href^="/explore/tags/"]');
return Array.from(hashtagElements).map(el => el.textContent.trim());
}
}Add your new platform to the getPlatformAdapter function:
function getPlatformAdapter(url) {
// LinkedIn support temporarily disabled
// if (url.includes('linkedin.com')) {
// return new LinkedInAdapter();
// } else
if (url.includes('twitter.com') || url.includes('x.com')) {
return new TwitterAdapter();
} else if (url.includes('reddit.com')) {
return new RedditAdapter();
} else if (url.includes('instagram.com')) {
return new InstagramAdapter();
}
return null;
}Add the new platform's URL pattern to manifest.json:
{
"content_scripts": [
{
"matches": [
"*://*.twitter.com/*",
"*://*.x.com/*",
"*://*.reddit.com/*",
"*://*.instagram.com/*"
],
"js": ["scripts/platform-adapters.js", "scripts/content.js"],
"run_at": "document_idle"
}
]
}If you want the popup to detect and display the new platform, update popup.js:
async detectPlatform() {
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const url = tab.url;
let platform = 'unknown';
let platformIcon = '🌐';
let platformName = 'Unknown Platform';
// LinkedIn support temporarily disabled
// if (url.includes('linkedin.com')) {
// platform = 'linkedin';
// platformIcon = '💼';
// platformName = 'LinkedIn';
// } else
if (url.includes('twitter.com') || url.includes('x.com')) {
platform = 'twitter';
platformIcon = '🐦';
platformName = 'Twitter/X';
} else if (url.includes('reddit.com')) {
platform = 'reddit';
platformIcon = '🤖';
platformName = 'Reddit';
} else if (url.includes('instagram.com')) {
platform = 'instagram';
platformIcon = '📷';
platformName = 'Instagram';
}
this.currentPlatform = platform;
this.updatePlatformDisplay(platformIcon, platformName);
} catch (error) {
console.error('Error detecting platform:', error);
this.updatePlatformDisplay('🌐', 'Unknown Platform');
}
}subreddit: The subreddit name (e.g., "programming")author: The post author's usernamepostType: Type of post ("text", "image", "video", "link")engagement: Object withupvotesandcommentscountsflair: Post flair/tag
author: The tweet author's nameisRetweet: Boolean indicating if it's a retweetengagement: Object withlikesandretweetscounts
- Temporarily Disabled: LinkedIn support has been temporarily disabled while we work on improving the feed extraction functionality.
author: The post author's namepostType: Type of post ("text", "article", "job", "image")engagement: Engagement count
- Fallback Logic: Include fallback extraction methods when primary selectors fail
- Error Handling: Return
nullfor missing data rather than throwing errors - Consistent Structure: Follow the same metadata structure as existing adapters when possible
- Testing: Test your adapter on different post types and UI variations
- Extensibility: Easy to add new platforms without touching core logic
- Maintainability: Platform-specific code is isolated and easy to update
- Consistency: All platforms follow the same interface
- Rich Context: Platform-specific metadata enables better prompt generation
- Future-Proof: New platforms can be added without breaking existing functionality