alwrity chatbot assistant, content scheduler, and content repurposing

This commit is contained in:
ajaysi
2025-06-02 00:00:18 +05:30
parent 889021c078
commit 5ca2fd5977
69 changed files with 13952 additions and 3279 deletions

View File

@@ -0,0 +1,283 @@
# Platform Adapters
A flexible and extensible system for managing content across different social media platforms and content management systems.
## Overview
The platform adapters system provides a unified interface for publishing, managing, and analyzing content across multiple platforms. It follows a modular architecture where each platform has its own adapter implementation while maintaining a consistent interface.
## Architecture
### Core Components
1. **Base Platform Adapter (`base.py`)**
- Abstract base class defining the interface for all platform adapters
- Common functionality and error handling
- Standardized response formatting
2. **Platform Manager (`manager.py`)**
- Central manager for handling multiple platform adapters
- Platform initialization and configuration
- Unified content publishing and management
3. **Unified Platform Adapter (`unified.py`)**
- Content adaptation across different platforms
- Platform-specific content generation
- Performance analytics and recommendations
### Current Implementations
#### Twitter Adapter (`twitter.py`)
- Full implementation of Twitter API integration
- Features:
- Tweet publishing with media support
- Content validation
- Analytics and engagement metrics
- Media upload handling
- Rate limit management
#### WordPress Adapter (TBD)
- Planned implementation of WordPress REST API integration
- Features:
- ⏳ Post creation and management
- ⏳ Page management
- ⏳ Media library integration
- ⏳ Category and tag management
- ⏳ Custom post type support
- ⏳ SEO metadata management
- ⏳ Comment moderation
- ⏳ User management
#### Wix Adapter (TBD)
- Planned implementation of Wix API integration
- Features:
- ⏳ Blog post management
- ⏳ Page content management
- ⏳ Media upload and management
- ⏳ SEO settings
- ⏳ Collection management
- ⏳ Form submissions handling
- ⏳ Site settings management
- ⏳ Analytics integration
## Features
### Core Features
- ✅ Multi-platform content publishing
- ✅ Content validation and optimization
- ✅ Analytics and performance tracking
- ✅ Media handling
- ✅ Error handling and logging
- ✅ Platform-specific content adaptation
### Platform-Specific Features
#### Twitter
- ✅ Tweet publishing
- ✅ Media attachments
- ✅ Analytics tracking
- ✅ Content validation
- ✅ Rate limit handling
#### Instagram (TBD)
- ⏳ Post creation
- ⏳ Story publishing
- ⏳ Hashtag optimization
- ⏳ Media handling
#### LinkedIn (TBD)
- ⏳ Post creation
- ⏳ Article publishing
- ⏳ Professional content optimization
- ⏳ Company page integration
#### Facebook (TBD)
- ⏳ Post creation
- ⏳ Page management
- ⏳ Audience targeting
- ⏳ Analytics integration
#### WordPress (TBD)
- ⏳ REST API integration
- ⏳ Content synchronization
- ⏳ Media management
- ⏳ SEO optimization
- ⏳ Custom post types
- ⏳ Plugin integration
#### Wix (TBD)
- ⏳ API integration
- ⏳ Content management
- ⏳ Media handling
- ⏳ SEO settings
- ⏳ Collection management
- ⏳ Analytics integration
## Configuration
Each platform adapter requires specific configuration parameters:
### Twitter Configuration
```python
{
'api_key': 'your_api_key',
'api_secret': 'your_api_secret',
'access_token': 'your_access_token',
'access_token_secret': 'your_access_token_secret'
}
```
### WordPress Configuration
```python
{
'site_url': 'https://your-wordpress-site.com',
'username': 'your_username',
'application_password': 'your_application_password',
'api_version': 'v2'
}
```
### Wix Configuration
```python
{
'site_id': 'your_site_id',
'api_key': 'your_api_key',
'access_token': 'your_access_token'
}
```
## Usage
### Basic Usage
```python
from lib.integrations.platform_adapters.manager import PlatformManager
# Initialize platform manager
config = {
'platforms': {
'twitter': {
'api_key': 'your_api_key',
'api_secret': 'your_api_secret',
'access_token': 'your_access_token',
'access_token_secret': 'your_access_token_secret'
},
'wordpress': {
'site_url': 'https://your-wordpress-site.com',
'username': 'your_username',
'application_password': 'your_application_password'
},
'wix': {
'site_id': 'your_site_id',
'api_key': 'your_api_key',
'access_token': 'your_access_token'
}
}
}
manager = PlatformManager(config)
# Publish content
content = {
'text': 'Hello, World!',
'media': [
{
'url': 'https://example.com/image.jpg',
'type': 'image'
}
]
}
result = await manager.publish_content(content, platforms=['twitter', 'wordpress', 'wix'])
```
## TBD Features
### Platform Support
- [ ] Instagram adapter implementation
- [ ] LinkedIn adapter implementation
- [ ] Facebook adapter implementation
- [ ] YouTube adapter implementation
- [ ] TikTok adapter implementation
- [ ] WordPress adapter implementation
- [ ] Wix adapter implementation
### Content Management
- [ ] Bulk content publishing
- [ ] Content scheduling
- [ ] Content templates
- [ ] A/B testing support
- [ ] Content versioning
- [ ] Cross-platform content synchronization
- [ ] CMS-specific content optimization
### Analytics
- [ ] Cross-platform analytics
- [ ] Custom metric tracking
- [ ] Automated reporting
- [ ] Performance optimization suggestions
- [ ] ROI tracking
- [ ] CMS-specific analytics integration
### Media Handling
- [ ] Advanced media optimization
- [ ] Media library management
- [ ] Automatic media resizing
- [ ] Media format conversion
- [ ] Media metadata management
- [ ] Cross-platform media synchronization
### Security
- [ ] OAuth2 implementation
- [ ] API key rotation
- [ ] Rate limit handling
- [ ] Error recovery
- [ ] Audit logging
- [ ] CMS-specific security features
## Contributing
1. Fork the repository
2. Create a feature branch
3. Implement your changes
4. Add tests
5. Submit a pull request
## Testing
Each platform adapter should include:
- Unit tests
- Integration tests
- Mock API responses
- Error handling tests
- Rate limit tests
- CMS-specific test cases
## Error Handling
The system implements standardized error handling:
- Platform-specific error mapping
- Retry mechanisms
- Error logging
- User-friendly error messages
- CMS-specific error handling
## Logging
Comprehensive logging system:
- Platform operations
- API calls
- Error tracking
- Performance metrics
- Debug information
- CMS-specific logging
## Dependencies
- Python 3.11+
- tweepy (for Twitter integration)
- requests
- loguru
- typing
- datetime
- wordpress-xmlrpc (for WordPress integration)
- wix-api-client (for Wix integration)

View File

@@ -0,0 +1,15 @@
"""
Platform adapters for content publishing and management.
"""
from .base import PlatformAdapter
from .manager import PlatformManager
from .twitter import TwitterAdapter
from .unified import UnifiedPlatformAdapter
__all__ = [
'PlatformAdapter',
'PlatformManager',
'TwitterAdapter',
'UnifiedPlatformAdapter'
]

View File

@@ -0,0 +1,157 @@
"""
Base platform adapter class.
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, List
from datetime import datetime
class PlatformAdapter(ABC):
"""Base class for platform-specific adapters."""
def __init__(self, config: Dict[str, Any]):
"""Initialize platform adapter with configuration."""
self.config = config
self.platform_name = self.__class__.__name__.replace('Adapter', '').upper()
@abstractmethod
async def publish_content(
self,
content: Dict[str, Any],
schedule_time: Optional[datetime] = None
) -> Dict[str, Any]:
"""Publish content to the platform."""
pass
@abstractmethod
async def get_content_status(
self,
content_id: str
) -> Dict[str, Any]:
"""Get the status of published content."""
pass
@abstractmethod
async def delete_content(
self,
content_id: str
) -> Dict[str, Any]:
"""Delete published content."""
pass
@abstractmethod
async def update_content(
self,
content_id: str,
updates: Dict[str, Any]
) -> Dict[str, Any]:
"""Update published content."""
pass
@abstractmethod
async def get_analytics(
self,
content_id: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> Dict[str, Any]:
"""Get analytics for published content."""
pass
@abstractmethod
async def validate_content(
self,
content: Dict[str, Any]
) -> Dict[str, Any]:
"""Validate content before publishing."""
pass
@abstractmethod
async def get_optimal_publish_time(
self,
content_type: str,
target_audience: Optional[Dict[str, Any]] = None
) -> datetime:
"""Get optimal publish time for content."""
pass
@abstractmethod
async def get_platform_limits(
self
) -> Dict[str, Any]:
"""Get platform-specific limits and constraints."""
pass
@abstractmethod
async def get_supported_content_types(
self
) -> List[str]:
"""Get list of supported content types."""
pass
@abstractmethod
async def get_platform_metrics(
self
) -> Dict[str, Any]:
"""Get platform-specific metrics and statistics."""
pass
def _format_error_response(
self,
error: Exception,
context: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Format error response."""
return {
'success': False,
'platform': self.platform_name,
'error': str(error),
'error_type': error.__class__.__name__,
'context': context or {}
}
def _format_success_response(
self,
data: Dict[str, Any],
context: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Format success response."""
return {
'success': True,
'platform': self.platform_name,
'data': data,
'context': context or {}
}
def _validate_config(self) -> None:
"""Validate platform configuration."""
required_fields = self.get_required_config_fields()
missing_fields = [
field for field in required_fields
if field not in self.config
]
if missing_fields:
raise ValueError(
f"Missing required configuration fields: {', '.join(missing_fields)}"
)
@classmethod
def get_required_config_fields(cls) -> List[str]:
"""Get list of required configuration fields."""
return []
@classmethod
def get_platform_name(cls) -> str:
"""Get platform name."""
return cls.__name__.replace('Adapter', '').upper()
@classmethod
def get_platform_description(cls) -> str:
"""Get platform description."""
return "Base platform adapter"
@classmethod
def get_platform_version(cls) -> str:
"""Get platform adapter version."""
return "1.0.0"

View File

@@ -0,0 +1,284 @@
"""
Platform manager for handling multiple platform adapters.
"""
import logging
from typing import Dict, Any, List, Optional, Type
from datetime import datetime
from .base import PlatformAdapter
from .twitter import TwitterAdapter
from .wix import WixAdapter
logger = logging.getLogger(__name__)
class PlatformManager:
"""Manages multiple platform adapters."""
def __init__(self, config: Dict[str, Any]):
"""Initialize platform manager with configuration."""
self.config = config
self.adapters: Dict[str, PlatformAdapter] = {}
self._initialize_adapters()
def _initialize_adapters(self) -> None:
"""Initialize platform adapters based on configuration."""
platform_configs = self.config.get('platforms', {})
for platform, config in platform_configs.items():
try:
adapter = self._create_adapter(platform, config)
if adapter:
self.adapters[platform] = adapter
logger.info(f"Initialized {platform} adapter")
except Exception as e:
logger.error(f"Failed to initialize {platform} adapter: {str(e)}")
def _create_adapter(
self,
platform: str,
config: Dict[str, Any]
) -> Optional[PlatformAdapter]:
"""Create platform adapter instance."""
adapter_map: Dict[str, Type[PlatformAdapter]] = {
'TWITTER': TwitterAdapter,
'WIX': WixAdapter,
# Add other platform adapters here
}
adapter_class = adapter_map.get(platform.upper())
if not adapter_class:
logger.warning(f"Unsupported platform: {platform}")
return None
try:
return adapter_class(config)
except Exception as e:
raise Exception(
f"Failed to create {platform} adapter: {str(e)}"
)
async def publish_content(
self,
content: Dict[str, Any],
platforms: List[str],
schedule_time: Optional[datetime] = None
) -> Dict[str, Dict[str, Any]]:
"""Publish content to multiple platforms."""
results = {}
for platform in platforms:
if platform not in self.adapters:
results[platform] = {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
continue
try:
result = await self.adapters[platform].publish_content(
content,
schedule_time
)
results[platform] = result
except Exception as e:
results[platform] = {
'success': False,
'error': str(e)
}
return results
async def get_content_status(
self,
content_id: str,
platform: str
) -> Dict[str, Any]:
"""Get content status from a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].get_content_status(content_id)
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def delete_content(
self,
content_id: str,
platform: str
) -> Dict[str, Any]:
"""Delete content from a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].delete_content(content_id)
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def update_content(
self,
content_id: str,
updates: Dict[str, Any],
platform: str
) -> Dict[str, Any]:
"""Update content on a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].update_content(
content_id,
updates
)
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def get_analytics(
self,
content_id: str,
platform: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> Dict[str, Any]:
"""Get analytics from a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].get_analytics(
content_id,
start_date,
end_date
)
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def validate_content(
self,
content: Dict[str, Any],
platform: str
) -> Dict[str, Any]:
"""Validate content for a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].validate_content(content)
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def get_optimal_publish_time(
self,
content_type: str,
platform: str,
target_audience: Optional[Dict[str, Any]] = None
) -> datetime:
"""Get optimal publish time for a specific platform."""
if platform not in self.adapters:
raise Exception(f"Platform adapter not found: {platform}")
return await self.adapters[platform].get_optimal_publish_time(
content_type,
target_audience
)
async def get_platform_limits(
self,
platform: str
) -> Dict[str, Any]:
"""Get platform limits for a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].get_platform_limits()
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def get_supported_content_types(
self,
platform: str
) -> List[str]:
"""Get supported content types for a specific platform."""
if platform not in self.adapters:
raise Exception(f"Platform adapter not found: {platform}")
return await self.adapters[platform].get_supported_content_types()
async def get_platform_metrics(
self,
platform: str
) -> Dict[str, Any]:
"""Get platform metrics for a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
try:
return await self.adapters[platform].get_platform_metrics()
except Exception as e:
return {
'success': False,
'error': str(e)
}
def get_available_platforms(self) -> List[str]:
"""Get list of available platform adapters."""
return list(self.adapters.keys())
def get_platform_info(self, platform: str) -> Dict[str, Any]:
"""Get information about a specific platform."""
if platform not in self.adapters:
return {
'success': False,
'error': f"Platform adapter not found: {platform}"
}
adapter = self.adapters[platform]
return {
'success': True,
'name': adapter.get_platform_name(),
'description': adapter.get_platform_description(),
'version': adapter.get_platform_version(),
'required_config': adapter.get_required_config_fields()
}

View File

@@ -0,0 +1,303 @@
"""
Twitter platform adapter implementation.
"""
from typing import Dict, Any, Optional, List
from datetime import datetime
import tweepy
from tweepy.models import Status
from .base import PlatformAdapter
class TwitterAdapter(PlatformAdapter):
"""Twitter platform adapter."""
def __init__(self, config: Dict[str, Any]):
"""Initialize Twitter adapter with configuration."""
super().__init__(config)
self._validate_config()
self._initialize_client()
def _initialize_client(self) -> None:
"""Initialize Twitter API client."""
try:
auth = tweepy.OAuthHandler(
self.config['api_key'],
self.config['api_secret']
)
auth.set_access_token(
self.config['access_token'],
self.config['access_token_secret']
)
self.client = tweepy.API(auth)
self.client.verify_credentials()
except Exception as e:
raise Exception(
f"Failed to initialize Twitter client: {str(e)}"
)
async def publish_content(
self,
content: Dict[str, Any],
schedule_time: Optional[datetime] = None
) -> Dict[str, Any]:
"""Publish content to Twitter."""
try:
# Validate content
validation = await self.validate_content(content)
if not validation.get('success'):
return validation
# Prepare tweet content
tweet_text = content.get('text', '')
media_ids = []
# Handle media attachments if present
if 'media' in content:
for media in content['media']:
media_id = self._upload_media(media)
if media_id:
media_ids.append(media_id)
# Create tweet
tweet = self.client.update_status(
status=tweet_text,
media_ids=media_ids if media_ids else None
)
return self._format_success_response({
'id': tweet.id_str,
'text': tweet.text,
'created_at': tweet.created_at.isoformat()
})
except Exception as e:
return self._format_error_response(
e,
{'content': content, 'schedule_time': schedule_time}
)
async def get_content_status(
self,
content_id: str
) -> Dict[str, Any]:
"""Get status of a tweet."""
try:
tweet = self.client.get_status(content_id)
return self._format_success_response({
'id': tweet.id_str,
'text': tweet.text,
'created_at': tweet.created_at.isoformat(),
'favorite_count': tweet.favorite_count,
'retweet_count': tweet.retweet_count
})
except Exception as e:
return self._format_error_response(
e,
{'content_id': content_id}
)
async def delete_content(
self,
content_id: str
) -> Dict[str, Any]:
"""Delete a tweet."""
try:
self.client.destroy_status(content_id)
return self._format_success_response({
'id': content_id,
'deleted': True
})
except Exception as e:
return self._format_error_response(
e,
{'content_id': content_id}
)
async def update_content(
self,
content_id: str,
updates: Dict[str, Any]
) -> Dict[str, Any]:
"""Update a tweet."""
try:
# Twitter doesn't support updating tweets
# We'll delete the old one and create a new one
await self.delete_content(content_id)
return await self.publish_content(updates)
except Exception as e:
return self._format_error_response(
e,
{
'content_id': content_id,
'updates': updates
}
)
async def get_analytics(
self,
content_id: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> Dict[str, Any]:
"""Get analytics for a tweet."""
try:
tweet = self.client.get_status(content_id)
return self._format_success_response({
'id': tweet.id_str,
'metrics': {
'favorites': tweet.favorite_count,
'retweets': tweet.retweet_count,
'replies': tweet.reply_count if hasattr(tweet, 'reply_count') else 0,
'impressions': tweet.impression_count if hasattr(tweet, 'impression_count') else 0
},
'engagement_rate': self._calculate_engagement_rate(tweet)
})
except Exception as e:
return self._format_error_response(
e,
{
'content_id': content_id,
'start_date': start_date,
'end_date': end_date
}
)
async def validate_content(
self,
content: Dict[str, Any]
) -> Dict[str, Any]:
"""Validate content before publishing."""
try:
# Check text length
text = content.get('text', '')
if len(text) > 280:
return self._format_error_response(
ValueError("Tweet text exceeds 280 characters"),
{'content': content}
)
# Check media attachments
media = content.get('media', [])
if len(media) > 4:
return self._format_error_response(
ValueError("Maximum 4 media attachments allowed"),
{'content': content}
)
return self._format_success_response({
'valid': True,
'content': content
})
except Exception as e:
return self._format_error_response(
e,
{'content': content}
)
async def get_optimal_publish_time(
self,
content_type: str,
target_audience: Optional[Dict[str, Any]] = None
) -> datetime:
"""Get optimal publish time for content."""
# Implement optimal time calculation based on:
# - Content type
# - Target audience timezone
# - Historical engagement data
# For now, return current time
return datetime.now()
async def get_platform_limits(
self
) -> Dict[str, Any]:
"""Get Twitter platform limits."""
return self._format_success_response({
'tweet_length': 280,
'media_attachments': 4,
'poll_options': 4,
'poll_duration': 10080, # 7 days in minutes
'rate_limits': {
'tweets_per_day': 2000,
'tweets_per_hour': 100
}
})
async def get_supported_content_types(
self
) -> List[str]:
"""Get list of supported content types."""
return ['TWEET', 'THREAD', 'POLL']
async def get_platform_metrics(
self
) -> Dict[str, Any]:
"""Get Twitter platform metrics."""
try:
account = self.client.verify_credentials()
return self._format_success_response({
'followers_count': account.followers_count,
'following_count': account.friends_count,
'tweets_count': account.statuses_count,
'account_created_at': account.created_at.isoformat()
})
except Exception as e:
return self._format_error_response(e)
def _calculate_engagement_rate(self, tweet: Status) -> float:
"""Calculate engagement rate for a tweet."""
try:
total_engagement = (
tweet.favorite_count +
tweet.retweet_count +
(tweet.reply_count if hasattr(tweet, 'reply_count') else 0)
)
followers = tweet.user.followers_count
return (total_engagement / followers * 100) if followers > 0 else 0.0
except Exception:
return 0.0
def _upload_media(self, media: Dict[str, Any]) -> Optional[str]:
"""Upload media to Twitter."""
try:
if 'url' in media:
# Download media from URL
response = requests.get(media['url'])
media_file = BytesIO(response.content)
elif 'file' in media:
# Use local file
media_file = open(media['file'], 'rb')
else:
return None
# Upload media
media_upload = self.client.media_upload(
filename=media.get('filename', 'media'),
file=media_file
)
return media_upload.media_id_string
except Exception as e:
logger.error(f"Failed to upload media: {str(e)}")
return None
@classmethod
def get_required_config_fields(cls) -> List[str]:
"""Get list of required configuration fields."""
return [
'api_key',
'api_secret',
'access_token',
'access_token_secret'
]
@classmethod
def get_platform_description(cls) -> str:
"""Get platform description."""
return "Twitter platform adapter for posting and managing tweets"
@classmethod
def get_platform_version(cls) -> str:
"""Get platform adapter version."""
return "1.0.0"

View File

@@ -0,0 +1,290 @@
"""
Unified platform adapter for content adaptation across different platforms.
"""
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from loguru import logger
from lib.utils.website_analyzer.analyzer import WebsiteAnalyzer
from lib.ai_seo_tools.content_gap_analysis.main import ContentGapAnalysis
from lib.ai_seo_tools.content_title_generator import ai_title_generator
from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main
from lib.ai_seo_tools.seo_structured_data import ai_structured_data
class UnifiedPlatformAdapter:
"""Unified adapter for different social media platforms."""
def __init__(self):
"""Initialize the platform adapter."""
self.platform_handlers = {
'instagram': self._handle_instagram,
'linkedin': self._handle_linkedin,
'twitter': self._handle_twitter,
'facebook': self._handle_facebook
}
logger.info("UnifiedPlatformAdapter initialized")
def generate_content(self, platform: str, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate content for a specific platform.
Args:
platform: Target platform
data: Content data
Returns:
Dictionary containing generated content
"""
try:
handler = self.platform_handlers.get(platform.lower())
if not handler:
raise ValueError(f"Unsupported platform: {platform}")
return handler(data)
except Exception as e:
error_msg = f"Error generating content for {platform}: {str(e)}"
logger.error(error_msg, exc_info=True)
return {
'error': error_msg,
'content': None
}
def get_content_performance(self, content_item: Dict[str, Any]) -> Dict[str, Any]:
"""Get performance metrics for content across platforms."""
try:
logger.info(f"Getting performance metrics for content: {content_item.get('title', 'Untitled')}")
# Get platform from content item
platform = content_item.get('platforms', ['Unknown'])[0]
# Initialize performance metrics
performance = {
'engagement_metrics': {
'likes': 0,
'comments': 0,
'shares': 0,
'reach': 0
},
'seo_metrics': {
'impressions': 0,
'clicks': 0,
'ctr': 0,
'position': 0
},
'conversion_metrics': {
'conversions': 0,
'conversion_rate': 0,
'revenue': 0
},
'platform_specific': {},
'performance_trends': [],
'recommendations': []
}
# Add platform-specific metrics
if platform == 'WEBSITE':
performance['platform_specific'] = {
'bounce_rate': 0,
'time_on_page': 0,
'page_views': 0
}
return performance
except Exception as e:
error_msg = f"Error getting content performance: {str(e)}"
logger.error(error_msg, exc_info=True)
return {
'error': error_msg,
'metrics': {},
'trends': {},
'recommendations': []
}
def _handle_instagram(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Instagram content generation."""
try:
# Generate Instagram-specific content
caption = metadesc_generator_main(data)
hashtags = self._generate_hashtags(data)
return {
'platform': 'instagram',
'content': {
'caption': caption,
'hashtags': hashtags,
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Instagram content: {str(e)}")
return {
'platform': 'instagram',
'error': str(e)
}
def _handle_linkedin(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle LinkedIn content generation."""
try:
# Generate LinkedIn-specific content
post = metadesc_generator_main(data)
return {
'platform': 'linkedin',
'content': {
'post': post,
'engagement_optimization': self._get_engagement_suggestions(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating LinkedIn content: {str(e)}")
return {
'platform': 'linkedin',
'error': str(e)
}
def _handle_twitter(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Twitter content generation."""
try:
# Generate Twitter-specific content
tweet = metadesc_generator_main(data)
hashtags = self._generate_hashtags(data)
return {
'platform': 'twitter',
'content': {
'tweet': tweet,
'hashtags': hashtags,
'thread_structure': self._get_thread_structure(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Twitter content: {str(e)}")
return {
'platform': 'twitter',
'error': str(e)
}
def _handle_facebook(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle Facebook content generation."""
try:
# Generate Facebook-specific content
post = metadesc_generator_main(data)
return {
'platform': 'facebook',
'content': {
'post': post,
'engagement_optimization': self._get_engagement_suggestions(data),
'media_suggestions': self._get_media_suggestions(data)
}
}
except Exception as e:
logger.error(f"Error generating Facebook content: {str(e)}")
return {
'platform': 'facebook',
'error': str(e)
}
def _generate_hashtags(self, data: Dict[str, Any]) -> List[str]:
"""Generate relevant hashtags for content."""
try:
# Extract keywords from content
keywords = data.get('keywords', [])
# Add platform-specific hashtags
platform = data.get('platform', '').lower()
platform_hashtags = {
'instagram': ['#instagood', '#photooftheday'],
'twitter': ['#trending', '#followme'],
'linkedin': ['#business', '#professional'],
'facebook': ['#social', '#community']
}.get(platform, [])
return keywords + platform_hashtags
except Exception as e:
logger.error(f"Error generating hashtags: {str(e)}")
return []
def _get_media_suggestions(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Get media suggestions for content."""
try:
# Generate media suggestions based on content type
content_type = data.get('type', 'post')
suggestions = []
if content_type == 'blog':
suggestions.append({
'type': 'featured_image',
'description': 'Main blog post image',
'dimensions': '1200x630'
})
elif content_type == 'social':
suggestions.append({
'type': 'post_image',
'description': 'Social media post image',
'dimensions': '1080x1080'
})
return suggestions
except Exception as e:
logger.error(f"Error getting media suggestions: {str(e)}")
return []
def _get_engagement_suggestions(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""Get engagement optimization suggestions."""
try:
return {
'best_posting_times': ['9:00 AM', '5:00 PM'],
'engagement_tips': [
'Ask questions to encourage comments',
'Use relevant hashtags',
'Include a clear call-to-action'
],
'content_length': {
'optimal': '150-200 characters',
'maximum': '300 characters'
}
}
except Exception as e:
logger.error(f"Error getting engagement suggestions: {str(e)}")
return {}
def _get_thread_structure(self, data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Get thread structure for Twitter threads."""
try:
content = data.get('content', '')
sentences = content.split('.')
thread = []
current_tweet = ''
for sentence in sentences:
if len(current_tweet + sentence) <= 280:
current_tweet += sentence + '.'
else:
if current_tweet:
thread.append({
'content': current_tweet.strip(),
'type': 'tweet'
})
current_tweet = sentence + '.'
if current_tweet:
thread.append({
'content': current_tweet.strip(),
'type': 'tweet'
})
return thread
except Exception as e:
logger.error(f"Error generating thread structure: {str(e)}")
return []

View File

@@ -0,0 +1,327 @@
"""
Wix platform adapter implementation.
"""
from io import BytesIO
from typing import Dict, Any, Optional, List
from datetime import datetime
import logging
from pathlib import Path
import requests
from .base import PlatformAdapter
from lib.integrations.wix.wix_api_client import WixAPIClient
logger = logging.getLogger(__name__)
class WixAdapter(PlatformAdapter):
"""Wix platform adapter."""
def __init__(self, config: Dict[str, Any]):
"""Initialize Wix adapter with configuration."""
super().__init__(config)
self._validate_config()
self._initialize_client()
def _initialize_client(self) -> None:
"""Initialize Wix API client."""
try:
self.client = WixAPIClient(
api_key=self.config.get('api_key'),
refresh_token=self.config.get('refresh_token'),
site_id=self.config.get('site_id')
)
logger.info("Successfully initialized Wix API client")
except Exception as e:
raise Exception(f"Failed to initialize Wix client: {str(e)}")
async def publish_content(
self,
content: Dict[str, Any],
schedule_time: Optional[datetime] = None
) -> Dict[str, Any]:
"""Publish content to Wix blog."""
try:
# Validate content
validation = await self.validate_content(content)
if not validation.get('success'):
return validation
# Prepare blog post data
post_data = {
'title': content.get('title', ''),
'content': content.get('content', ''),
'excerpt': content.get('excerpt', ''),
'slug': content.get('slug', ''),
'tags': content.get('tags', []),
'categories': content.get('categories', []),
'seo': content.get('seo', {}),
'publish_date': schedule_time.isoformat() if schedule_time else None
}
# Handle media attachments
media_ids = []
if 'media' in content:
for media in content['media']:
media_id = await self._upload_media(media)
if media_id:
media_ids.append(media_id)
# Create blog post
post = self.client.create_post(post_data)
# Add media to post if any
if media_ids:
self.client.add_media_to_post(post['id'], media_ids)
return self._format_success_response({
'id': post['id'],
'title': post['title'],
'url': post['url'],
'created_at': post['created_at']
})
except Exception as e:
return self._format_error_response(
e,
{'content': content, 'schedule_time': schedule_time}
)
async def get_content_status(
self,
content_id: str
) -> Dict[str, Any]:
"""Get status of a blog post."""
try:
post = self.client.get_post(content_id)
return self._format_success_response({
'id': post['id'],
'title': post['title'],
'status': post['status'],
'url': post['url'],
'created_at': post['created_at'],
'updated_at': post['updated_at'],
'published_at': post.get('published_at')
})
except Exception as e:
return self._format_error_response(
e,
{'content_id': content_id}
)
async def delete_content(
self,
content_id: str
) -> Dict[str, Any]:
"""Delete a blog post."""
try:
self.client.delete_post(content_id)
return self._format_success_response({
'id': content_id,
'deleted': True
})
except Exception as e:
return self._format_error_response(
e,
{'content_id': content_id}
)
async def update_content(
self,
content_id: str,
updates: Dict[str, Any]
) -> Dict[str, Any]:
"""Update a blog post."""
try:
post = self.client.update_post(content_id, updates)
return self._format_success_response({
'id': post['id'],
'title': post['title'],
'url': post['url'],
'updated_at': post['updated_at']
})
except Exception as e:
return self._format_error_response(
e,
{
'content_id': content_id,
'updates': updates
}
)
async def get_analytics(
self,
content_id: str,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> Dict[str, Any]:
"""Get analytics for a blog post."""
try:
analytics = self.client.get_post_analytics(
content_id,
start_date,
end_date
)
return self._format_success_response({
'id': content_id,
'metrics': {
'views': analytics.get('views', 0),
'unique_visitors': analytics.get('unique_visitors', 0),
'average_time_on_page': analytics.get('average_time_on_page', 0),
'bounce_rate': analytics.get('bounce_rate', 0)
}
})
except Exception as e:
return self._format_error_response(
e,
{
'content_id': content_id,
'start_date': start_date,
'end_date': end_date
}
)
async def validate_content(
self,
content: Dict[str, Any]
) -> Dict[str, Any]:
"""Validate content before publishing."""
try:
# Check required fields
required_fields = ['title', 'content']
missing_fields = [
field for field in required_fields
if field not in content
]
if missing_fields:
return self._format_error_response(
ValueError(f"Missing required fields: {', '.join(missing_fields)}"),
{'content': content}
)
# Check content length
if len(content['content']) > 100000: # Wix limit
return self._format_error_response(
ValueError("Content exceeds maximum length of 100,000 characters"),
{'content': content}
)
# Check media attachments
media = content.get('media', [])
if len(media) > 20: # Wix limit
return self._format_error_response(
ValueError("Maximum 20 media attachments allowed"),
{'content': content}
)
return self._format_success_response({
'valid': True,
'content': content
})
except Exception as e:
return self._format_error_response(
e,
{'content': content}
)
async def get_optimal_publish_time(
self,
content_type: str,
target_audience: Optional[Dict[str, Any]] = None
) -> datetime:
"""Get optimal publish time for content."""
# Implement optimal time calculation based on:
# - Content type
# - Target audience timezone
# - Historical engagement data
# For now, return current time
return datetime.now()
async def get_platform_limits(
self
) -> Dict[str, Any]:
"""Get Wix platform limits."""
return self._format_success_response({
'content_length': 100000,
'media_attachments': 20,
'tags_per_post': 50,
'categories_per_post': 10,
'rate_limits': {
'posts_per_day': 100,
'media_uploads_per_day': 1000
}
})
async def get_supported_content_types(
self
) -> List[str]:
"""Get list of supported content types."""
return ['BLOG_POST', 'PAGE', 'COLLECTION_ITEM']
async def get_platform_metrics(
self
) -> Dict[str, Any]:
"""Get Wix platform metrics."""
try:
site_stats = self.client.get_site_statistics()
return self._format_success_response({
'total_posts': site_stats.get('total_posts', 0),
'total_views': site_stats.get('total_views', 0),
'total_comments': site_stats.get('total_comments', 0),
'average_engagement': site_stats.get('average_engagement', 0)
})
except Exception as e:
return self._format_error_response(e)
async def _upload_media(
self,
media: Dict[str, Any]
) -> Optional[str]:
"""Upload media to Wix."""
try:
if 'url' in media:
# Download media from URL
response = requests.get(media['url'])
media_file = BytesIO(response.content)
filename = media.get('filename', 'media')
elif 'file' in media:
# Use local file
file_path = Path(media['file'])
media_file = open(file_path, 'rb')
filename = file_path.name
else:
return None
# Upload media
media_id = self.client.upload_media(
file=media_file,
filename=filename,
mime_type=media.get('mime_type')
)
return media_id
except Exception as e:
logger.error(f"Failed to upload media: {str(e)}")
return None
@classmethod
def get_required_config_fields(cls) -> List[str]:
"""Get list of required configuration fields."""
return [
'api_key',
'refresh_token',
'site_id'
]
@classmethod
def get_platform_description(cls) -> str:
"""Get platform description."""
return "Wix platform adapter for managing blog posts and content"
@classmethod
def get_platform_version(cls) -> str:
"""Get platform adapter version."""
return "1.0.0"