Files
ALwrity/Roadmap TBDs/TWITTER_IMPLEMENTATION_PLAN.md
2025-04-21 21:07:35 +05:30

150 KiB

Twitter Integration Implementation Plan

This document outlines a comprehensive plan to implement and enhance the Twitter features in AI-Writer, transforming the "coming soon" features into fully functional components using Tweepy and user-provided API keys.

Current State Analysis

The current Twitter functionality in AI-Writer includes:

  • A Twitter dashboard with multiple planned features
  • Only the "Smart Tweet Generator" is currently active
  • The tweet generator creates content but lacks real Twitter integration
  • Performance metrics are simulated rather than based on real data
  • No actual posting, scheduling, or analytics capabilities

Implementation Strategy

We'll implement the Twitter features in multiple phases, focusing on delivering value incrementally while building toward a comprehensive Twitter management solution.

Phase 1: Twitter Authentication & Basic Integration

1.1 Twitter API Authentication System

Create a secure system for users to connect their Twitter accounts:

# lib/integrations/twitter/auth.py

import tweepy
import streamlit as st
from typing import Dict, Optional, Any
import os
import json
from pathlib import Path
import time
from loguru import logger

# Configuration paths
CONFIG_DIR = Path(__file__).parent.parent.parent.parent / 'config' / 'twitter'
CONFIG_DIR.mkdir(exist_ok=True, parents=True)

def get_twitter_auth_url() -> str:
    """Generate Twitter OAuth URL for user authentication."""
    try:
        # Get API keys from session state or environment
        consumer_key = st.session_state.get('twitter_consumer_key', os.getenv('TWITTER_CONSUMER_KEY', ''))
        consumer_secret = st.session_state.get('twitter_consumer_secret', os.getenv('TWITTER_CONSUMER_SECRET', ''))
        
        if not consumer_key or not consumer_secret:
            logger.error("Twitter API keys not found")
            return ""
        
        # Initialize OAuth handler
        oauth1_user_handler = tweepy.OAuth1UserHandler(
            consumer_key, consumer_secret,
            callback="http://localhost:8501/twitter/callback"
        )
        
        # Get authorization URL
        auth_url = oauth1_user_handler.get_authorization_url()
        
        # Store OAuth handler in session state
        st.session_state.twitter_oauth_handler = oauth1_user_handler
        
        return auth_url
    except Exception as e:
        logger.error(f"Error generating Twitter auth URL: {str(e)}")
        return ""

def handle_twitter_callback(oauth_verifier: str) -> bool:
    """Handle Twitter OAuth callback."""
    try:
        # Get OAuth handler from session state
        oauth_handler = st.session_state.get('twitter_oauth_handler')
        if not oauth_handler:
            logger.error("OAuth handler not found in session state")
            return False
        
        # Get access tokens
        access_token, access_token_secret = oauth_handler.get_access_token(oauth_verifier)
        
        # Store tokens in session state
        st.session_state.twitter_access_token = access_token
        st.session_state.twitter_access_token_secret = access_token_secret
        
        # Create API client
        api = create_twitter_api()
        
        # Get user info
        user = api.verify_credentials()
        
        # Store user info
        st.session_state.twitter_user = {
            'id': user.id,
            'screen_name': user.screen_name,
            'name': user.name,
            'profile_image_url': user.profile_image_url,
            'followers_count': user.followers_count,
            'friends_count': user.friends_count
        }
        
        # Save credentials to file
        save_twitter_credentials(
            user.id,
            access_token,
            access_token_secret
        )
        
        return True
    except Exception as e:
        logger.error(f"Error handling Twitter callback: {str(e)}")
        return False

def create_twitter_api() -> tweepy.API:
    """Create Twitter API client."""
    try:
        # Get API keys and tokens
        consumer_key = st.session_state.get('twitter_consumer_key', os.getenv('TWITTER_CONSUMER_KEY', ''))
        consumer_secret = st.session_state.get('twitter_consumer_secret', os.getenv('TWITTER_CONSUMER_SECRET', ''))
        access_token = st.session_state.get('twitter_access_token', '')
        access_token_secret = st.session_state.get('twitter_access_token_secret', '')
        
        # Create auth handler
        auth = tweepy.OAuth1UserHandler(
            consumer_key, consumer_secret,
            access_token, access_token_secret
        )
        
        # Create API client
        api = tweepy.API(auth)
        
        return api
    except Exception as e:
        logger.error(f"Error creating Twitter API client: {str(e)}")
        raise

def create_twitter_client() -> tweepy.Client:
    """Create Twitter API v2 client."""
    try:
        # Get API keys and tokens
        consumer_key = st.session_state.get('twitter_consumer_key', os.getenv('TWITTER_CONSUMER_KEY', ''))
        consumer_secret = st.session_state.get('twitter_consumer_secret', os.getenv('TWITTER_CONSUMER_SECRET', ''))
        access_token = st.session_state.get('twitter_access_token', '')
        access_token_secret = st.session_state.get('twitter_access_token_secret', '')
        bearer_token = st.session_state.get('twitter_bearer_token', os.getenv('TWITTER_BEARER_TOKEN', ''))
        
        # Create client
        client = tweepy.Client(
            bearer_token=bearer_token,
            consumer_key=consumer_key,
            consumer_secret=consumer_secret,
            access_token=access_token,
            access_token_secret=access_token_secret
        )
        
        return client
    except Exception as e:
        logger.error(f"Error creating Twitter client: {str(e)}")
        raise

def save_twitter_credentials(user_id: int, access_token: str, access_token_secret: str) -> bool:
    """Save Twitter credentials to file."""
    try:
        # Create user-specific credentials file
        creds_file = CONFIG_DIR / f"{user_id}.json"
        
        # Save credentials
        with open(creds_file, 'w') as f:
            json.dump({
                'access_token': access_token,
                'access_token_secret': access_token_secret,
                'timestamp': time.time()
            }, f)
        
        return True
    except Exception as e:
        logger.error(f"Error saving Twitter credentials: {str(e)}")
        return False

def load_twitter_credentials(user_id: int) -> Dict[str, str]:
    """Load Twitter credentials from file."""
    try:
        # Get user-specific credentials file
        creds_file = CONFIG_DIR / f"{user_id}.json"
        
        # Check if file exists
        if not creds_file.exists():
            logger.warning(f"No credentials found for user {user_id}")
            return {}
        
        # Load credentials
        with open(creds_file, 'r') as f:
            creds = json.load(f)
        
        # Update session state
        st.session_state.twitter_access_token = creds['access_token']
        st.session_state.twitter_access_token_secret = creds['access_token_secret']
        
        return creds
    except Exception as e:
        logger.error(f"Error loading Twitter credentials: {str(e)}")
        return {}

def is_authenticated() -> bool:
    """Check if user is authenticated with Twitter."""
    return (
        'twitter_access_token' in st.session_state and
        'twitter_access_token_secret' in st.session_state and
        'twitter_user' in st.session_state
    )

def logout() -> None:
    """Log out user from Twitter."""
    if 'twitter_access_token' in st.session_state:
        del st.session_state.twitter_access_token
    if 'twitter_access_token_secret' in st.session_state:
        del st.session_state.twitter_access_token_secret
    if 'twitter_user' in st.session_state:
        del st.session_state.twitter_user
    if 'twitter_oauth_handler' in st.session_state:
        del st.session_state.twitter_oauth_handler

1.2 Twitter API Key Management UI

Create a UI for users to enter and manage their Twitter API keys:

# lib/integrations/twitter/api_key_manager.py

import streamlit as st
from typing import Dict, Optional
import os
from loguru import logger

def render_twitter_api_key_manager() -> None:
    """Render Twitter API key management UI."""
    st.markdown("## Twitter API Configuration")
    st.markdown("""
        To use Twitter integration features, you need to provide your Twitter API keys.
        You can get these by creating a Twitter Developer account and setting up a project.
    """)
    
    # Create tabs for different authentication methods
    tab1, tab2 = st.tabs(["Basic Authentication", "Advanced Settings"])
    
    with tab1:
        # Get existing keys from session state or environment
        consumer_key = st.session_state.get('twitter_consumer_key', os.getenv('TWITTER_CONSUMER_KEY', ''))
        consumer_secret = st.session_state.get('twitter_consumer_secret', os.getenv('TWITTER_CONSUMER_SECRET', ''))
        bearer_token = st.session_state.get('twitter_bearer_token', os.getenv('TWITTER_BEARER_TOKEN', ''))
        
        # Input fields for API keys
        new_consumer_key = st.text_input(
            "Consumer Key (API Key)",
            value=consumer_key,
            type="password",
            help="Your Twitter API consumer key"
        )
        
        new_consumer_secret = st.text_input(
            "Consumer Secret (API Secret)",
            value=consumer_secret,
            type="password",
            help="Your Twitter API consumer secret"
        )
        
        new_bearer_token = st.text_input(
            "Bearer Token",
            value=bearer_token,
            type="password",
            help="Your Twitter API bearer token"
        )
        
        # Save button
        if st.button("Save API Keys", use_container_width=True):
            # Update session state
            st.session_state.twitter_consumer_key = new_consumer_key
            st.session_state.twitter_consumer_secret = new_consumer_secret
            st.session_state.twitter_bearer_token = new_bearer_token
            
            # Show success message
            st.success("Twitter API keys saved successfully!")
    
    with tab2:
        st.markdown("### Advanced API Settings")
        
        # Callback URL
        st.text_input(
            "Callback URL",
            value="http://localhost:8501/twitter/callback",
            disabled=True,
            help="Use this URL in your Twitter Developer Portal"
        )
        
        # API usage limits
        st.info("""
            **Twitter API Rate Limits:**
            - Standard API: 500,000 tweets/month
            - Essential API: 10,000 tweets/month
            
            Make sure your Twitter Developer account has the appropriate access level for your needs.
        """)
        
        # Help resources
        st.markdown("""
            **Need help?**
            - [Twitter Developer Portal](https://developer.twitter.com)
            - [API Documentation](https://developer.twitter.com/en/docs)
            - [Rate Limits](https://developer.twitter.com/en/docs/twitter-api/rate-limits)
        """)

1.3 Twitter Account Connection UI

Create a UI for users to connect their Twitter accounts:

# lib/integrations/twitter/account_manager.py

import streamlit as st
from typing import Dict, Optional
from .auth import get_twitter_auth_url, is_authenticated, logout, create_twitter_api
import tweepy
from loguru import logger

def render_twitter_account_manager() -> None:
    """Render Twitter account management UI."""
    st.markdown("## Twitter Account")
    
    # Check if API keys are configured
    if not st.session_state.get('twitter_consumer_key') or not st.session_state.get('twitter_consumer_secret'):
        st.warning("Please configure your Twitter API keys first.")
        return
    
    # Check if user is authenticated
    if is_authenticated():
        # Get user info
        user = st.session_state.twitter_user
        
        # Display user info
        col1, col2, col3 = st.columns([1, 3, 1])
        
        with col1:
            st.image(user['profile_image_url'], width=80)
        
        with col2:
            st.markdown(f"**{user['name']}** (@{user['screen_name']})")
            st.markdown(f"Followers: {user['followers_count']} | Following: {user['friends_count']}")
        
        with col3:
            if st.button("Disconnect", key="disconnect_twitter"):
                logout()
                st.experimental_rerun()
        
        # Account status
        st.success("✅ Your Twitter account is connected and ready to use.")
        
        # Account actions
        st.markdown("### Quick Actions")
        col1, col2 = st.columns(2)
        
        with col1:
            if st.button("View Profile", use_container_width=True):
                try:
                    # Get API client
                    api = create_twitter_api()
                    
                    # Get user timeline
                    tweets = api.user_timeline(count=5)
                    
                    # Display tweets
                    st.markdown("#### Recent Tweets")
                    for tweet in tweets:
                        st.markdown(f"""
                            <div style='padding: 10px; border-radius: 5px; background-color: #f0f2f6; margin-bottom: 10px;'>
                                <p>{tweet.text}</p>
                                <small>Posted on {tweet.created_at.strftime('%Y-%m-%d %H:%M')}</small>
                            </div>
                        """, unsafe_allow_html=True)
                except Exception as e:
                    logger.error(f"Error fetching tweets: {str(e)}")
                    st.error(f"Error fetching tweets: {str(e)}")
        
        with col2:
            if st.button("Check API Limits", use_container_width=True):
                try:
                    # Get API client
                    api = create_twitter_api()
                    
                    # Get rate limit status
                    limits = api.rate_limit_status()
                    
                    # Display limits
                    st.markdown("#### API Rate Limits")
                    
                    # Timeline limits
                    timeline_limit = limits['resources']['statuses']['/statuses/user_timeline']
                    st.markdown(f"**Timeline:** {timeline_limit['remaining']}/{timeline_limit['limit']} requests remaining")
                    
                    # Search limits
                    search_limit = limits['resources']['search']['/search/tweets']
                    st.markdown(f"**Search:** {search_limit['remaining']}/{search_limit['limit']} requests remaining")
                    
                    # Tweet posting limits
                    st.markdown("**Tweet Posting:** Limited by your Twitter API access level")
                except Exception as e:
                    logger.error(f"Error checking API limits: {str(e)}")
                    st.error(f"Error checking API limits: {str(e)}")
    else:
        # Get auth URL
        auth_url = get_twitter_auth_url()
        
        if auth_url:
            # Display connect button
            st.markdown("""
                <a href="{auth_url}" target="_self" style="text-decoration: none;">
                    <div style="display: inline-flex; align-items: center; background-color: #1DA1F2; color: white; padding: 0.5rem 1rem; border-radius: 5px; cursor: pointer;">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="white" style="margin-right: 8px;">
                            <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
                        </svg>
                        Connect Twitter Account
                    </div>
                </a>
            """.format(auth_url=auth_url), unsafe_allow_html=True)
            
            st.info("Click the button above to connect your Twitter account.")
        else:
            st.error("Failed to generate authentication URL. Please check your API keys.")

1.4 Twitter Callback Handler

Create a handler for the Twitter OAuth callback:

# lib/integrations/twitter/callback_handler.py

import streamlit as st
from urllib.parse import parse_qs, urlparse
from .auth import handle_twitter_callback
from loguru import logger

def handle_oauth_callback():
    """Handle Twitter OAuth callback in Streamlit."""
    # Get current URL
    query_params = st.experimental_get_query_params()
    
    # Check if this is a callback
    if 'oauth_token' in query_params and 'oauth_verifier' in query_params:
        oauth_verifier = query_params['oauth_verifier'][0]
        
        # Handle callback
        success = handle_twitter_callback(oauth_verifier)
        
        if success:
            # Clear query parameters to avoid reprocessing
            st.experimental_set_query_params()
            
            # Show success message
            st.success("Successfully connected Twitter account!")
            
            # Redirect to main page
            st.experimental_rerun()
        else:
            st.error("Failed to authenticate with Twitter. Please try again.")
    
    # Check for error
    if 'denied' in query_params:
        st.error("Twitter authentication was denied.")
        st.experimental_set_query_params()

Phase 2: Enhanced Tweet Generator with Real Twitter Integration

2.1 Improved Tweet Generator with Real Data

Enhance the tweet generator to use real Twitter data:

# lib/ai_writers/twitter_writers/tweet_generator/enhanced_tweet_generator.py

import streamlit as st
import re
import json
import time
from typing import Dict, List, Tuple, Optional
import random
import emoji
from datetime import datetime
import tweepy

from .....gpt_providers.text_generation.main_text_generation import llm_text_gen
from ....integrations.twitter.auth import create_twitter_api, create_twitter_client, is_authenticated

# Constants
MAX_TWEET_LENGTH = 280
EMOJI_CATEGORIES = {
    "Humorous": ["😄", "😂", "🤣", "😊", "😉", "😎", "🤪", "😜", "🤓", "😇"],
    "Informative": ["📚", "📊", "📈", "🔍", "💡", "📝", "📋", "🔎", "📖", "📑"],
    "Inspirational": ["✨", "🌟", "💫", "⭐", "🔥", "💪", "🙌", "👏", "💯", "🎯"],
    "Serious": ["🤔", "💭", "🧐", "📢", "🔔", "⚖️", "🎓", "📊", "🔬", "📰"],
    "Casual": ["👋", "👍", "🙋", "💁", "🤗", "👌", "✌️", "🤝", "👊", "🙏"]
}

def count_characters(text: str) -> int:
    """Count characters in tweet, accounting for emojis."""
    return len(text)

def extract_hashtags(text: str) -> List[str]:
    """Extract hashtags from tweet text."""
    return re.findall(r'#\w+', text)

def get_trending_hashtags(location_id: int = 1) -> List[str]:
    """Get trending hashtags from Twitter API."""
    try:
        if not is_authenticated():
            return []
        
        # Get API client
        api = create_twitter_api()
        
        # Get trending topics
        trends = api.get_place_trends(location_id)
        
        # Extract hashtags
        hashtags = [trend['name'] for trend in trends[0]['trends'] if trend['name'].startswith('#')]
        
        return hashtags[:10]  # Return top 10 hashtags
    except Exception as e:
        st.error(f"Error fetching trending hashtags: {str(e)}")
        return []

def suggest_hashtags(topic: str, tone: str, use_trending: bool = False) -> List[str]:
    """Suggest relevant hashtags based on topic and tone."""
    # Enhanced hashtag suggestions based on topic and tone
    base_hashtags = {
        "professional": ["#Business", "#Leadership", "#Innovation"],
        "casual": ["#Life", "#Fun", "#Trending"],
        "informative": ["#Learn", "#Tips", "#HowTo"],
        "humorous": ["#Funny", "#LOL", "#Humor"],
        "inspirational": ["#Motivation", "#Success", "#Growth"]
    }
    
    topic_hashtags = {
        "tech": ["#Technology", "#TechNews", "#Innovation"],
        "business": ["#Business", "#Entrepreneurship", "#Startup"],
        "marketing": ["#Marketing", "#DigitalMarketing", "#SocialMedia"],
        "education": ["#Education", "#Learning", "#Knowledge"],
        "health": ["#Health", "#Wellness", "#Fitness"]
    }
    
    # Combine base and topic hashtags
    suggested = base_hashtags.get(tone.lower(), []) + topic_hashtags.get(topic.lower(), [])
    
    # Add trending hashtags if requested
    if use_trending and is_authenticated():
        trending = get_trending_hashtags()
        suggested = list(set(suggested + trending))
    
    return list(set(suggested))[:5]  # Return unique hashtags, max 5

def get_optimal_posting_time() -> Dict[str, Any]:
    """Get optimal posting time based on follower activity."""
    try:
        if not is_authenticated():
            return {
                "time": datetime.now().strftime("%H:%M"),
                "day": datetime.now().strftime("%A"),
                "confidence": "low"
            }
        
        # Get API client
        api = create_twitter_api()
        
        # Get user timeline
        tweets = api.user_timeline(count=100)
        
        # Analyze engagement by hour and day
        hour_engagement = {}
        day_engagement = {}
        
        for tweet in tweets:
            hour = tweet.created_at.hour
            day = tweet.created_at.strftime("%A")
            engagement = tweet.favorite_count + tweet.retweet_count
            
            if hour not in hour_engagement:
                hour_engagement[hour] = []
            hour_engagement[hour].append(engagement)
            
            if day not in day_engagement:
                day_engagement[day] = []
            day_engagement[day].append(engagement)
        
        # Calculate average engagement by hour and day
        hour_avg_engagement = {h: sum(e)/len(e) for h, e in hour_engagement.items() if e}
        day_avg_engagement = {d: sum(e)/len(e) for d, e in day_engagement.items() if e}
        
        # Find optimal hour and day
        optimal_hour = max(hour_avg_engagement.items(), key=lambda x: x[1])[0] if hour_avg_engagement else datetime.now().hour
        optimal_day = max(day_avg_engagement.items(), key=lambda x: x[1])[0] if day_avg_engagement else datetime.now().strftime("%A")
        
        # Calculate confidence
        confidence = "high" if len(tweets) >= 50 else "medium" if len(tweets) >= 20 else "low"
        
        return {
            "time": f"{optimal_hour:02d}:00",
            "day": optimal_day,
            "confidence": confidence
        }
    except Exception as e:
        st.error(f"Error calculating optimal posting time: {str(e)}")
        return {
            "time": datetime.now().strftime("%H:%M"),
            "day": datetime.now().strftime("%A"),
            "confidence": "low"
        }

def analyze_audience() -> Dict[str, Any]:
    """Analyze Twitter audience for content targeting."""
    try:
        if not is_authenticated():
            return {
                "follower_count": 0,
                "engagement_rate": 0,
                "top_interests": [],
                "active_hours": []
            }
        
        # Get API client
        api = create_twitter_api()
        
        # Get user info
        user = api.verify_credentials()
        
        # Get user timeline
        tweets = api.user_timeline(count=100)
        
        # Calculate engagement rate
        total_engagement = sum(tweet.favorite_count + tweet.retweet_count for tweet in tweets)
        engagement_rate = total_engagement / (len(tweets) * user.followers_count) if tweets and user.followers_count else 0
        
        # Analyze active hours
        hour_activity = {}
        for tweet in tweets:
            hour = tweet.created_at.hour
            if hour not in hour_activity:
                hour_activity[hour] = 0
            hour_activity[hour] += 1
        
        active_hours = sorted(hour_activity.items(), key=lambda x: x[1], reverse=True)[:3]
        active_hours = [f"{hour:02d}:00" for hour, _ in active_hours]
        
        # Get follower interests (simplified)
        top_interests = ["Technology", "Business", "Marketing"]  # Would require more complex analysis
        
        return {
            "follower_count": user.followers_count,
            "engagement_rate": engagement_rate * 100,  # Convert to percentage
            "top_interests": top_interests,
            "active_hours": active_hours
        }
    except Exception as e:
        st.error(f"Error analyzing audience: {str(e)}")
        return {
            "follower_count": 0,
            "engagement_rate": 0,
            "top_interests": [],
            "active_hours": []
        }

def generate_tweet_variations(
    hook: str,
    target_audience: str,
    tone: str,
    call_to_action: str = "",
    keywords: str = "",
    length: str = "medium",
    num_variations: int = 3,
    use_ai: bool = True
) -> List[Dict]:
    """Generate multiple tweet variations with AI and Twitter data."""
    # Enhanced prompt template with Twitter-specific guidance
    prompt_template = f"""
    Create {num_variations} engaging tweet variations with the following parameters:
    - Hook/Topic: {hook}
    - Target Audience: {target_audience}
    - Tone: {tone}
    - Call to Action: {call_to_action}
    - Keywords: {keywords}
    - Length: {length}
    
    Each tweet should:
    1. Start with an attention-grabbing hook
    2. Include relevant hashtags (max 2-3)
    3. Use appropriate emojis (1-2 max)
    4. End with a clear call-to-action
    5. Stay within Twitter's 280 character limit
    6. Match the specified tone and audience
    
    Format each tweet as a JSON object with:
    - text: The tweet content
    - hashtags: List of suggested hashtags
    - emojis: List of suggested emojis
    """
    
    if use_ai:
        # Use AI to generate tweets
        try:
            response = llm_text_gen(prompt_template)
            
            # Parse JSON response
            try:
                tweets = json.loads(response)
                if not isinstance(tweets, list):
                    tweets = [tweets]
            except:
                # Handle non-JSON response by extracting tweet text
                tweet_texts = re.findall(r'"text"\s*:\s*"([^"]+)"', response)
                tweets = [{"text": text, "hashtags": extract_hashtags(text), "emojis": []} for text in tweet_texts]
            
            # Ensure we have the requested number of variations
            while len(tweets) < num_variations:
                tweets.append({
                    "text": f"{hook} {call_to_action}",
                    "hashtags": suggest_hashtags(keywords, tone),
                    "emojis": []
                })
            
            # Add engagement score
            for tweet in tweets:
                tweet["engagement_score"] = random.randint(60, 95)
            
            return tweets[:num_variations]
        except Exception as e:
            st.error(f"Error generating tweets with AI: {str(e)}")
            # Fall back to template-based generation
    
    # Template-based generation (fallback)
    templates = [
        "{emoji} {hook} {hashtags} {cta}",
        "{hook} {emoji} {hashtags} {cta}",
        "{hashtags} {hook} {emoji} {cta}"
    ]
    
    tweets = []
    for i in range(num_variations):
        template = templates[i % len(templates)]
        emoji_list = EMOJI_CATEGORIES.get(tone.capitalize(), EMOJI_CATEGORIES["Casual"])
        emoji_str = random.choice(emoji_list)
        hashtag_list = suggest_hashtags(keywords, tone)
        hashtag_str = " ".join(hashtag_list[:2])
        
        tweet_text = template.format(
            emoji=emoji_str,
            hook=hook,
            hashtags=hashtag_str,
            cta=call_to_action
        )
        
        tweets.append({
            "text": tweet_text,
            "hashtags": hashtag_list,
            "emojis": [emoji_str],
            "engagement_score": random.randint(60, 95)
        })
    
    return tweets

def post_tweet(tweet_text: str) -> Dict[str, Any]:
    """Post a tweet to Twitter."""
    try:
        if not is_authenticated():
            return {"success": False, "error": "Not authenticated with Twitter"}
        
        # Get API client
        api = create_twitter_api()
        
        # Post tweet
        status = api.update_status(tweet_text)
        
        return {
            "success": True,
            "tweet_id": status.id,
            "created_at": status.created_at.isoformat()
        }
    except Exception as e:
        return {"success": False, "error": str(e)}

def schedule_tweet(tweet_text: str, scheduled_time: datetime) -> Dict[str, Any]:
    """Schedule a tweet for later posting."""
    try:
        # Store scheduled tweet in session state
        if "scheduled_tweets" not in st.session_state:
            st.session_state.scheduled_tweets = []
        
        tweet_id = f"scheduled_{int(time.time())}"
        
        st.session_state.scheduled_tweets.append({
            "id": tweet_id,
            "text": tweet_text,
            "scheduled_time": scheduled_time.isoformat(),
            "status": "scheduled"
        })
        
        return {
            "success": True,
            "id": tweet_id,
            "scheduled_time": scheduled_time.isoformat()
        }
    except Exception as e:
        return {"success": False, "error": str(e)}

def enhanced_tweet_generator():
    """Enhanced Smart Tweet Generator with Twitter integration."""
    st.title("✨ Enhanced Tweet Generator")
    st.markdown("Create and post engaging tweets with AI and Twitter data")
    
    # Check if connected to Twitter
    twitter_connected = is_authenticated()
    
    # Twitter connection status
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
        
        # Show audience insights
        with st.expander("Audience Insights", expanded=False):
            audience = analyze_audience()
            
            col1, col2, col3 = st.columns(3)
            with col1:
                st.metric("Followers", f"{audience['follower_count']:,}")
            with col2:
                st.metric("Engagement Rate", f"{audience['engagement_rate']:.2f}%")
            with col3:
                st.metric("Active Hours", ", ".join(audience['active_hours']) if audience['active_hours'] else "Unknown")
            
            if audience['top_interests']:
                st.markdown(f"**Top Interests:** {', '.join(audience['top_interests'])}")
    else:
        st.warning("Connect your Twitter account to access advanced features")
    
    # Input section with improved UI
    with st.expander("Tweet Parameters", expanded=True):
        col1, col2 = st.columns(2)
        
        with col1:
            hook = st.text_area("Tweet Hook/Topic", 
                              placeholder="Enter your main message or topic...",
                              help="The main message or topic of your tweet")
            
            target_audience = st.selectbox(
                "Target Audience",
                ["Professionals", "Students", "General"],
                help="Select your target audience"
            )
            
            tone = st.radio(
                "Tweet Tone",
                ["Professional", "Casual", "Informative", "Humorous", "Inspirational"],
                horizontal=True,
                help="Choose the tone for your tweet"
            )
        
        with col2:
            call_to_action = st.text_input(
                "Call to Action",
                placeholder="e.g., Learn more, Follow us...",
                help="What action do you want your audience to take?"
            )
            
            keywords = st.text_input(
                "Keywords/Hashtags",
                placeholder="Enter keywords separated by commas",
                help="Keywords to include in your tweet"
            )
            
            length = st.select_slider(
                "Tweet Length",
                options=["short", "medium", "long"],
                value="medium",
                help="Choose your desired tweet length"
            )
            
            num_variations = st.slider(
                "Number of Variations",
                min_value=1,
                max_value=5,
                value=3,
                help="How many tweet variations would you like to generate?"
            )
    
    # Advanced options
    with st.expander("Advanced Options", expanded=False):
        use_trending = st.checkbox("Include trending hashtags", value=False)
        
        if twitter_connected:
            # Get optimal posting time
            optimal_time = get_optimal_posting_time()
            
            st.markdown(f"""
                **Optimal Posting Time:** {optimal_time['day']} at {optimal_time['time']}
                (Confidence: {optimal_time['confidence']})
            """)
    
    # Generate button with loading state
    if st.button("Generate Tweets", use_container_width=True):
        with st.spinner("Generating tweet variations..."):
            tweets = generate_tweet_variations(
                hook, target_audience, tone,
                call_to_action, keywords, length,
                num_variations, use_ai=True
            )
            
            # Store tweets in session state
            st.session_state.generated_tweets = tweets
            
            # Display tweets
            st.markdown("### Generated Tweets")
            
            for i, tweet in enumerate(tweets):
                with st.container():
                    st.markdown(f"""
                        <div style='padding: 20px; border-radius: 10px; background-color: #f0f2f6; margin-bottom: 20px;'>
                            <h3 style='margin: 0;'>Tweet Variation {i + 1}</h3>
                            <p style='margin: 10px 0; font-size: 1.1em;'>{tweet['text']}</p>
                            <div style='display: flex; gap: 10px;'>
                                <span style='background-color: #e1e4e8; padding: 5px 10px; border-radius: 15px; font-size: 0.8em;'>
                                    Score: {tweet['engagement_score']}%
                                </span>
                                <span style='background-color: #e1e4e8; padding: 5px 10px; border-radius: 15px; font-size: 0.8em;'>
                                    {count_characters(tweet['text'])}/280 chars
                                </span>
                            </div>
                        </div>
                    """, unsafe_allow_html=True)
                    
                    # Action buttons
                    col1, col2, col3 = st.columns(3)
                    
                    with col1:
                        if st.button(f"Copy Tweet {i + 1}", key=f"copy_{i}"):
                            st.code(tweet['text'])
                            st.success("Tweet copied to clipboard!")
                    
                    with col2:
                        if twitter_connected and st.button(f"Post Now {i + 1}", key=f"post_{i}"):
                            result = post_tweet(tweet['text'])
                            if result['success']:
                                st.success("Tweet posted successfully!")
                            else:
                                st.error(f"Error posting tweet: {result['error']}")
                    
                    with col3:
                        if twitter_connected and st.button(f"Schedule {i + 1}", key=f"schedule_{i}"):
                            # Show scheduling options
                            st.session_state.tweet_to_schedule = tweet['text']
                            st.session_state.scheduling_tweet_index = i
                            st.experimental_rerun()
            
            # Export options
            st.markdown("### 📥 Export Options")
            col1, col2 = st.columns(2)
            with col1:
                if st.button("Export as JSON"):
                    st.download_button(
                        "Download JSON",
                        data=json.dumps(tweets, indent=2),
                        file_name=f"tweets_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
                        mime="application/json"
                    )
            with col2:
                if st.button("Copy All Tweets"):
                    tweet_texts = "\n\n".join(tweet["text"] for tweet in tweets)
                    st.code(tweet_texts)
    
    # Handle tweet scheduling
    if "tweet_to_schedule" in st.session_state:
        st.markdown("### Schedule Tweet")
        st.markdown(f"**Tweet to schedule:** {st.session_state.tweet_to_schedule}")
        
        col1, col2 = st.columns(2)
        with col1:
            schedule_date = st.date_input("Date", value=datetime.now().date())
        with col2:
            schedule_time = st.time_input("Time", value=datetime.now().time())
        
        scheduled_datetime = datetime.combine(schedule_date, schedule_time)
        
        if st.button("Confirm Schedule"):
            result = schedule_tweet(st.session_state.tweet_to_schedule, scheduled_datetime)
            if result['success']:
                st.success(f"Tweet scheduled for {scheduled_datetime.strftime('%Y-%m-%d %H:%M')}")
                del st.session_state.tweet_to_schedule
                del st.session_state.scheduling_tweet_index
            else:
                st.error(f"Error scheduling tweet: {result['error']}")

2.2 Tweet Performance Predictor

Implement the Tweet Performance Predictor feature:

# lib/ai_writers/twitter_writers/tweet_performance/performance_predictor.py

import streamlit as st
import pandas as pd
import numpy as np
from typing import Dict, List, Any
import json
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import OneHotEncoder
import pickle
import os
from pathlib import Path

from ....integrations.twitter.auth import create_twitter_api, is_authenticated

# Constants
MODEL_DIR = Path(__file__).parent.parent.parent.parent.parent / 'models' / 'twitter'
MODEL_DIR.mkdir(exist_ok=True, parents=True)
MODEL_PATH = MODEL_DIR / 'performance_predictor.pkl'

def get_tweet_history() -> pd.DataFrame:
    """Get tweet history from Twitter API."""
    try:
        if not is_authenticated():
            return pd.DataFrame()
        
        # Get API client
        api = create_twitter_api()
        
        # Get user timeline
        tweets = api.user_timeline(count=200, tweet_mode='extended')
        
        # Create DataFrame
        data = []
        for tweet in tweets:
            # Skip retweets
            if hasattr(tweet, 'retweeted_status'):
                continue
            
            # Extract tweet data
            tweet_data = {
                'id': tweet.id,
                'text': tweet.full_text,
                'created_at': tweet.created_at,
                'likes': tweet.favorite_count,
                'retweets': tweet.retweet_count,
                'replies': 0,  # Not available in standard API
                'impressions': 0,  # Not available in standard API
                'hashtags': [h['text'] for h in tweet.entities.get('hashtags', [])],
                'mentions': [m['screen_name'] for m in tweet.entities.get('user_mentions', [])],
                'urls': [u['expanded_url'] for u in tweet.entities.get('urls', [])],
                'media': 1 if 'media' in tweet.entities else 0,
                'hour': tweet.created_at.hour,
                'day': tweet.created_at.weekday(),
                'length': len(tweet.full_text)
            }
            
            # Calculate engagement
            tweet_data['engagement'] = tweet_data['likes'] + tweet_data['retweets']
            
            data.append(tweet_data)
        
        # Create DataFrame
        df = pd.DataFrame(data)
        
        return df
    except Exception as e:
        st.error(f"Error fetching tweet history: {str(e)}")
        return pd.DataFrame()

def train_performance_model(df: pd.DataFrame) -> Any:
    """Train a model to predict tweet performance."""
    try:
        if df.empty:
            return None
        
        # Feature engineering
        X = pd.DataFrame({
            'hour': df['hour'],
            'day': df['day'],
            'length': df['length'],
            'hashtag_count': df['hashtags'].apply(len),
            'mention_count': df['mentions'].apply(len),
            'url_count': df['urls'].apply(len),
            'has_media': df['media']
        })
        
        # Target variable
        y = df['engagement']
        
        # Train model
        model = RandomForestRegressor(n_estimators=100, random_state=42)
        model.fit(X, y)
        
        # Save model
        with open(MODEL_PATH, 'wb') as f:
            pickle.dump(model, f)
        
        return model
    except Exception as e:
        st.error(f"Error training model: {str(e)}")
        return None

def load_performance_model() -> Any:
    """Load the performance prediction model."""
    try:
        if MODEL_PATH.exists():
            with open(MODEL_PATH, 'rb') as f:
                model = pickle.load(f)
            return model
        else:
            return None
    except Exception as e:
        st.error(f"Error loading model: {str(e)}")
        return None

def predict_performance(
    tweet_text: str,
    hour: int,
    day: int,
    has_media: bool = False
) -> Dict[str, Any]:
    """Predict tweet performance."""
    try:
        # Load model
        model = load_performance_model()
        
        if model is None:
            # No model available, use heuristics
            return predict_performance_heuristic(tweet_text, hour, day, has_media)
        
        # Feature extraction
        hashtags = len([w for w in tweet_text.split() if w.startswith('#')])
        mentions = len([w for w in tweet_text.split() if w.startswith('@')])
        urls = len([w for w in tweet_text.split() if w.startswith('http')])
        length = len(tweet_text)
        
        # Create feature vector
        X = pd.DataFrame({
            'hour': [hour],
            'day': [day],
            'length': [length],
            'hashtag_count': [hashtags],
            'mention_count': [mentions],
            'url_count': [urls],
            'has_media': [1 if has_media else 0]
        })
        
        # Make prediction
        engagement = model.predict(X)[0]
        
        # Calculate confidence
        confidence = "high" if model.n_estimators >= 100 else "medium"
        
        return {
            "predicted_engagement": engagement,
            "confidence": confidence,
            "factors": {
                "time_factor": get_time_factor(hour, day),
                "content_factor": get_content_factor(tweet_text),
                "media_factor": 1.5 if has_media else 1.0
            }
        }
    except Exception as e:
        st.error(f"Error predicting performance: {str(e)}")
        return predict_performance_heuristic(tweet_text, hour, day, has_media)

def predict_performance_heuristic(
    tweet_text: str,
    hour: int,
    day: int,
    has_media: bool = False
) -> Dict[str, Any]:
    """Predict tweet performance using heuristics."""
    # Time factor (higher for business hours on weekdays)
    time_factor = get_time_factor(hour, day)
    
    # Content factor
    content_factor = get_content_factor(tweet_text)
    
    # Media factor
    media_factor = 1.5 if has_media else 1.0
    
    # Base engagement (arbitrary value)
    base_engagement = 10
    
    # Calculate predicted engagement
    predicted_engagement = base_engagement * time_factor * content_factor * media_factor
    
    return {
        "predicted_engagement": predicted_engagement,
        "confidence": "low",
        "factors": {
            "time_factor": time_factor,
            "content_factor": content_factor,
            "media_factor": media_factor
        }
    }

def get_time_factor(hour: int, day: int) -> float:
    """Calculate time factor for engagement prediction."""
    # Weekday factor (higher for weekdays)
    weekday_factor = 1.0 if day < 5 else 0.8
    
    # Hour factor (higher for business hours)
    if 9 <= hour <= 17:
        hour_factor = 1.0
    elif 7 <= hour <= 8 or 18 <= hour <= 21:
        hour_factor = 0.8
    else:
        hour_factor = 0.6
    
    return weekday_factor * hour_factor

def get_content_factor(tweet_text: str) -> float:
    """Calculate content factor for engagement prediction."""
    # Length factor (higher for medium-length tweets)
    length = len(tweet_text)
    if 70 <= length <= 140:
        length_factor = 1.0
    elif length < 70:
        length_factor = 0.8
    else:
        length_factor = 0.9
    
    # Hashtag factor (higher for 1-2 hashtags)
    hashtags = len([w for w in tweet_text.split() if w.startswith('#')])
    if 1 <= hashtags <= 2:
        hashtag_factor = 1.0
    elif hashtags == 0:
        hashtag_factor = 0.8
    else:
        hashtag_factor = 0.7  # Too many hashtags
    
    # Question factor (higher for tweets with questions)
    question_factor = 1.2 if '?' in tweet_text else 1.0
    
    # Call to action factor
    cta_words = ['follow', 'retweet', 'like', 'share', 'comment', 'check', 'click', 'read']
    cta_factor = 1.1 if any(word in tweet_text.lower() for word in cta_words) else 1.0
    
    return length_factor * hashtag_factor * question_factor * cta_factor

def get_best_posting_times() -> List[Dict[str, Any]]:
    """Get best posting times based on historical data."""
    try:
        # Get tweet history
        df = get_tweet_history()
        
        if df.empty:
            # Return default recommendations
            return [
                {"day": "Monday", "hour": 12, "confidence": "low"},
                {"day": "Wednesday", "hour": 15, "confidence": "low"},
                {"day": "Friday", "hour": 10, "confidence": "low"}
            ]
        
        # Group by day and hour
        df['day_name'] = df['created_at'].dt.day_name()
        engagement_by_time = df.groupby(['day_name', 'hour'])['engagement'].mean().reset_index()
        
        # Sort by engagement
        engagement_by_time = engagement_by_time.sort_values('engagement', ascending=False)
        
        # Get top 3 times
        top_times = engagement_by_time.head(3)
        
        # Format results
        results = []
        for _, row in top_times.iterrows():
            results.append({
                "day": row['day_name'],
                "hour": row['hour'],
                "confidence": "high" if len(df) >= 100 else "medium" if len(df) >= 50 else "low"
            })
        
        return results
    except Exception as e:
        st.error(f"Error getting best posting times: {str(e)}")
        return [
            {"day": "Monday", "hour": 12, "confidence": "low"},
            {"day": "Wednesday", "hour": 15, "confidence": "low"},
            {"day": "Friday", "hour": 10, "confidence": "low"}
        ]

def render_performance_predictor():
    """Render the Tweet Performance Predictor UI."""
    st.title("📊 Tweet Performance Predictor")
    st.markdown("Predict engagement and find the best time to post your tweets")
    
    # Check if connected to Twitter
    twitter_connected = is_authenticated()
    
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
        
        # Get tweet history
        with st.spinner("Analyzing your tweet history..."):
            df = get_tweet_history()
        
        if not df.empty:
            # Train model if needed
            if not MODEL_PATH.exists():
                with st.spinner("Training prediction model..."):
                    train_performance_model(df)
            
            # Show historical performance
            with st.expander("Your Tweet Performance", expanded=True):
                # Engagement over time
                st.markdown("#### Engagement Over Time")
                fig = px.line(
                    df.sort_values('created_at'),
                    x='created_at',
                    y='engagement',
                    title="Tweet Engagement History"
                )
                st.plotly_chart(fig, use_container_width=True)
                
                # Engagement by day and hour
                st.markdown("#### Engagement by Day and Hour")
                df['day_name'] = df['created_at'].dt.day_name()
                pivot = df.pivot_table(
                    index='day_name',
                    columns='hour',
                    values='engagement',
                    aggfunc='mean'
                )
                
                fig = px.imshow(
                    pivot,
                    labels=dict(x="Hour of Day", y="Day of Week", color="Avg. Engagement"),
                    x=pivot.columns,
                    y=pivot.index,
                    color_continuous_scale="Viridis"
                )
                st.plotly_chart(fig, use_container_width=True)
                
                # Content analysis
                st.markdown("#### Content Analysis")
                col1, col2 = st.columns(2)
                
                with col1:
                    # Engagement by tweet length
                    df['length_bin'] = pd.cut(df['length'], bins=[0, 70, 140, 210, 280], labels=['0-70', '71-140', '141-210', '211-280'])
                    length_engagement = df.groupby('length_bin')['engagement'].mean().reset_index()
                    
                    fig = px.bar(
                        length_engagement,
                        x='length_bin',
                        y='engagement',
                        title="Engagement by Tweet Length"
                    )
                    st.plotly_chart(fig, use_container_width=True)
                
                with col2:
                    # Engagement by hashtag count
                    df['hashtag_count'] = df['hashtags'].apply(len)
                    df['hashtag_bin'] = pd.cut(df['hashtag_count'], bins=[-1, 0, 1, 2, 10], labels=['0', '1', '2', '3+'])
                    hashtag_engagement = df.groupby('hashtag_bin')['engagement'].mean().reset_index()
                    
                    fig = px.bar(
                        hashtag_engagement,
                        x='hashtag_bin',
                        y='engagement',
                        title="Engagement by Hashtag Count"
                    )
                    st.plotly_chart(fig, use_container_width=True)
        
        # Best posting times
        best_times = get_best_posting_times()
        
        st.markdown("### 🕒 Best Times to Post")
        for i, time in enumerate(best_times):
            st.markdown(f"**{i+1}.** {time['day']} at {time['hour']}:00 (Confidence: {time['confidence']})")
    else:
        st.warning("Connect your Twitter account to access performance prediction features")
        
        # Show demo data
        st.markdown("### Demo Data")
        st.markdown("Here's how the performance predictor works with sample data:")
        
        # Sample engagement chart
        dates = pd.date_range(start='2023-01-01', periods=30)
        engagements = np.random.normal(loc=20, scale=10, size=30)
        engagements = np.abs(engagements)  # Make all values positive
        
        df = pd.DataFrame({
            'date': dates,
            'engagement': engagements
        })
        
        fig = px.line(
            df,
            x='date',
            y='engagement',
            title="Sample Tweet Engagement History"
        )
        st.plotly_chart(fig, use_container_width=True)
    
    # Tweet input for prediction
    st.markdown("### Predict Tweet Performance")
    
    tweet_text = st.text_area(
        "Enter your tweet",
        placeholder="Type your tweet here...",
        max_chars=280
    )
    
    col1, col2, col3 = st.columns(3)
    
    with col1:
        day = st.selectbox(
            "Day of week",
            ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
        )
        day_num = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].index(day)
    
    with col2:
        hour = st.slider("Hour of day", 0, 23, 12)
    
    with col3:
        has_media = st.checkbox("Include media", value=False)
    
    if st.button("Predict Performance", use_container_width=True):
        if tweet_text:
            with st.spinner("Predicting performance..."):
                prediction = predict_performance(tweet_text, hour, day_num, has_media)
                
                # Display prediction
                st.markdown("### Prediction Results")
                
                col1, col2 = st.columns(2)
                
                with col1:
                    st.metric(
                        "Predicted Engagement",
                        f"{prediction['predicted_engagement']:.1f}",
                        delta=None
                    )
                
                with col2:
                    st.markdown(f"**Confidence:** {prediction['confidence'].title()}")
                
                # Factors affecting prediction
                st.markdown("#### Factors Affecting Prediction")
                
                factors = prediction['factors']
                factor_df = pd.DataFrame({
                    'Factor': ['Time of posting', 'Content quality', 'Media inclusion'],
                    'Impact': [
                        factors['time_factor'],
                        factors['content_factor'],
                        factors['media_factor']
                    ]
                })
                
                fig = px.bar(
                    factor_df,
                    x='Factor',
                    y='Impact',
                    title="Impact Factors"
                )
                st.plotly_chart(fig, use_container_width=True)
                
                # Recommendations
                st.markdown("#### Recommendations to Improve Performance")
                
                recommendations = []
                
                if factors['time_factor'] < 0.9:
                    recommendations.append("Consider posting during weekdays between 9 AM and 5 PM for better engagement.")
                
                if factors['content_factor'] < 0.9:
                    if len(tweet_text) > 140:
                        recommendations.append("Try shortening your tweet to 70-140 characters for optimal engagement.")
                    
                    hashtags = len([w for w in tweet_text.split() if w.startswith('#')])
                    if hashtags == 0:
                        recommendations.append("Add 1-2 relevant hashtags to increase visibility.")
                    elif hashtags > 2:
                        recommendations.append("Reduce the number of hashtags to 1-2 for better engagement.")
                    
                    if '?' not in tweet_text:
                        recommendations.append("Consider adding a question to encourage responses.")
                    
                    cta_words = ['follow', 'retweet', 'like', 'share', 'comment', 'check', 'click', 'read']
                    if not any(word in tweet_text.lower() for word in cta_words):
                        recommendations.append("Add a clear call-to-action to encourage engagement.")
                
                if not has_media:
                    recommendations.append("Including an image or video could increase engagement by up to 50%.")
                
                if recommendations:
                    for rec in recommendations:
                        st.info(rec)
                else:
                    st.success("Your tweet is well-optimized for engagement!")
        else:
            st.error("Please enter a tweet to predict performance.")

Phase 3: Content Strategy Tools

3.1 Content Calendar Generator

Implement the Content Calendar Generator feature:

# lib/ai_writers/twitter_writers/content_strategy/calendar_generator.py

import streamlit as st
import pandas as pd
import numpy as np
from typing import Dict, List, Any
import json
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
import calendar
import random
from io import BytesIO

from ....integrations.twitter.auth import create_twitter_api, is_authenticated
from ....gpt_providers.text_generation.main_text_generation import llm_text_gen

def generate_content_themes(industry: str, num_themes: int = 5) -> List[str]:
    """Generate content themes based on industry."""
    # Predefined themes by industry
    industry_themes = {
        "Technology": [
            "Product Updates", "Tech Tips", "Industry News", "Customer Success Stories",
            "Behind the Scenes", "Tech Trends", "How-To Guides", "Tech Humor",
            "Industry Events", "Q&A Sessions"
        ],
        "Marketing": [
            "Marketing Tips", "Case Studies", "Industry Trends", "Tool Recommendations",
            "Marketing Metrics", "Campaign Spotlights", "Marketing Humor", "Expert Interviews",
            "Marketing Events", "Strategy Insights"
        ],
        "Education": [
            "Learning Resources", "Student Spotlights", "Education News", "Teaching Tips",
            "Research Highlights", "Campus Events", "Educational Humor", "Alumni Stories",
            "Faculty Spotlights", "Educational Trends"
        ],
        "Health": [
            "Health Tips", "Wellness Advice", "Medical News", "Patient Stories",
            "Research Updates", "Health Myths Debunked", "Nutrition Tips", "Exercise Guides",
            "Mental Health Awareness", "Healthcare Innovations"
        ],
        "Finance": [
            "Financial Tips", "Market Updates", "Investment Strategies", "Financial Education",
            "Economic News", "Savings Guides", "Financial Planning", "Success Stories",
            "Industry Trends", "Q&A Sessions"
        ]
    }
    
    # Get themes for the selected industry or use AI to generate them
    if industry in industry_themes:
        themes = industry_themes[industry]
        return random.sample(themes, min(num_themes, len(themes)))
    else:
        # Use AI to generate themes
        prompt = f"""
        Generate {num_themes} content themes for Twitter posts in the {industry} industry.
        Each theme should be a short phrase (2-4 words) that describes a category of content.
        Return the themes as a JSON array of strings.
        """
        
        try:
            response = llm_text_gen(prompt)
            
            # Try to parse as JSON
            try:
                themes = json.loads(response)
                if isinstance(themes, list):
                    return themes[:num_themes]
            except:
                # Extract themes using regex
                import re
                themes = re.findall(r'"([^"]+)"', response)
                return themes[:num_themes]
        except Exception as e:
            st.error(f"Error generating themes: {str(e)}")
            
            # Return generic themes
            generic_themes = [
                "Industry News", "Tips & Tricks", "Behind the Scenes",
                "Customer Stories", "Product Highlights"
            ]
            return generic_themes[:num_themes]

def generate_content_ideas(theme: str, industry: str, num_ideas: int = 3) -> List[str]:
    """Generate content ideas for a theme."""
    # Use AI to generate content ideas
    prompt = f"""
    Generate {num_ideas} specific Twitter post ideas for the theme "{theme}" in the {industry} industry.
    Each idea should be a brief description (not the actual tweet) of what the post will be about.
    Keep each idea under 100 characters.
    Return the ideas as a JSON array of strings.
    """
    
    try:
        response = llm_text_gen(prompt)
        
        # Try to parse as JSON
        try:
            ideas = json.loads(response)
            if isinstance(ideas, list):
                return ideas[:num_ideas]
        except:
            # Extract ideas using regex
            import re
            ideas = re.findall(r'"([^"]+)"', response)
            return ideas[:num_ideas] if ideas else [f"{theme} idea {i+1}" for i in range(num_ideas)]
    except Exception as e:
        st.error(f"Error generating ideas: {str(e)}")
        
        # Return generic ideas
        return [f"{theme} idea {i+1}" for i in range(num_ideas)]

def create_content_calendar(
    start_date: datetime,
    end_date: datetime,
    posts_per_week: int,
    themes: List[str],
    industry: str
) -> pd.DataFrame:
    """Create a content calendar with themes and ideas."""
    # Calculate date range
    date_range = pd.date_range(start=start_date, end=end_date)
    
    # Filter for weekdays if needed
    if posts_per_week <= 5:
        date_range = date_range[date_range.dayofweek < 5]  # Monday to Friday
    
    # Calculate posting frequency
    if posts_per_week < 5:
        # Select specific days
        days_to_post = random.sample(range(5), posts_per_week)  # Random weekdays
        date_range = date_range[date_range.dayofweek.isin(days_to_post)]
    
    # Limit to the requested frequency
    posting_dates = []
    current_week = -1
    posts_this_week = 0
    
    for date in date_range:
        week_num = date.isocalendar()[1]
        
        if week_num != current_week:
            current_week = week_num
            posts_this_week = 0
        
        if posts_this_week < posts_per_week:
            posting_dates.append(date)
            posts_this_week += 1
    
    # Create calendar dataframe
    calendar_data = []
    
    for date in posting_dates:
        # Assign theme (rotate through themes)
        theme_index = len(calendar_data) % len(themes)
        theme = themes[theme_index]
        
        # Generate content ideas
        ideas = generate_content_ideas(theme, industry, num_ideas=1)
        
        calendar_data.append({
            'date': date,
            'day': date.day_name(),
            'week': date.isocalendar()[1],
            'theme': theme,
            'content_idea': ideas[0] if ideas else "",
            'status': 'Planned'
        })
    
    # Create DataFrame
    df = pd.DataFrame(calendar_data)
    
    return df

def export_calendar_to_excel(df: pd.DataFrame) -> BytesIO:
    """Export content calendar to Excel."""
    # Create Excel writer
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    
    # Write DataFrame to Excel
    df.to_excel(writer, sheet_name='Content Calendar', index=False)
    
    # Get workbook and worksheet
    workbook = writer.book
    worksheet = writer.sheets['Content Calendar']
    
    # Add formats
    header_format = workbook.add_format({
        'bold': True,
        'text_wrap': True,
        'valign': 'top',
        'fg_color': '#D7E4BC',
        'border': 1
    })
    
    date_format = workbook.add_format({
        'num_format': 'yyyy-mm-dd',
        'border': 1
    })
    
    cell_format = workbook.add_format({
        'text_wrap': True,
        'border': 1
    })
    
    # Write headers with format
    for col_num, value in enumerate(df.columns.values):
        worksheet.write(0, col_num, value, header_format)
    
    # Format columns
    worksheet.set_column('A:A', 12, date_format)  # Date column
    worksheet.set_column('B:B', 10, cell_format)  # Day column
    worksheet.set_column('C:C', 8, cell_format)   # Week column
    worksheet.set_column('D:D', 15, cell_format)  # Theme column
    worksheet.set_column('E:E', 40, cell_format)  # Content idea column
    worksheet.set_column('F:F', 10, cell_format)  # Status column
    
    # Close the writer
    writer.close()
    
    # Return the Excel file
    output.seek(0)
    return output

def render_calendar_generator():
    """Render the Content Calendar Generator UI."""
    st.title("🗓️ Content Calendar Generator")
    st.markdown("Create a strategic Twitter content calendar with themes and ideas")
    
    # Check if connected to Twitter
    twitter_connected = is_authenticated()
    
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
    
    # Calendar settings
    st.markdown("### Calendar Settings")
    
    col1, col2 = st.columns(2)
    
    with col1:
        start_date = st.date_input("Start Date", value=datetime.now().date())
        
        industry = st.selectbox(
            "Industry",
            ["Technology", "Marketing", "Education", "Health", "Finance", "Other"],
            index=0
        )
        
        if industry == "Other":
            industry = st.text_input("Specify Industry")
    
    with col2:
        duration = st.selectbox(
            "Duration",
            ["1 week", "2 weeks", "1 month", "3 months"],
            index=2
        )
        
        # Calculate end date based on duration
        if duration == "1 week":
            end_date = start_date + timedelta(days=6)
        elif duration == "2 weeks":
            end_date = start_date + timedelta(days=13)
        elif duration == "1 month":
            end_date = start_date + timedelta(days=29)
        else:  # 3 months
            end_date = start_date + timedelta(days=89)
        
        posts_per_week = st.slider(
            "Posts per Week",
            min_value=1,
            max_value=7,
            value=3
        )
    
    # Theme settings
    st.markdown("### Content Themes")
    
    num_themes = st.slider(
        "Number of Themes",
        min_value=1,
        max_value=10,
        value=5
    )
    
    # Generate themes button
    if st.button("Generate Themes", key="generate_themes"):
        with st.spinner("Generating content themes..."):
            themes = generate_content_themes(industry, num_themes)
            st.session_state.content_themes = themes
    
    # Display and edit themes
    if "content_themes" in st.session_state:
        themes = st.session_state.content_themes
        
        # Create columns for themes
        cols = st.columns(min(5, len(themes)))
        
        # Display each theme in a column
        for i, theme in enumerate(themes):
            col_index = i % len(cols)
            with cols[col_index]:
                new_theme = st.text_input(f"Theme {i+1}", value=theme, key=f"theme_{i}")
                themes[i] = new_theme
        
        st.session_state.content_themes = themes
    else:
        st.info("Click 'Generate Themes' to create content themes for your calendar")
    
    # Generate calendar button
    if st.button("Generate Calendar", use_container_width=True):
        if "content_themes" in st.session_state and st.session_state.content_themes:
            with st.spinner("Creating your content calendar..."):
                # Create calendar
                calendar_df = create_content_calendar(
                    start_date=start_date,
                    end_date=end_date,
                    posts_per_week=posts_per_week,
                    themes=st.session_state.content_themes,
                    industry=industry
                )
                
                # Store in session state
                st.session_state.content_calendar = calendar_df
                
                # Display calendar
                st.markdown("### Your Content Calendar")
                
                # Calendar view
                calendar_view = st.radio(
                    "View",
                    ["Table", "Calendar View"],
                    horizontal=True
                )
                
                if calendar_view == "Table":
                    # Display as table
                    st.dataframe(
                        calendar_df,
                        column_config={
                            "date": st.column_config.DateColumn("Date", format="YYYY-MM-DD"),
                            "day": "Day",
                            "week": "Week",
                            "theme": "Theme",
                            "content_idea": st.column_config.TextColumn("Content Idea", width="large"),
                            "status": "Status"
                        },
                        hide_index=True
                    )
                else:
                    # Display as calendar
                    # Group by week
                    weeks = calendar_df.groupby('week')
                    
                    for week_num, week_data in weeks:
                        st.markdown(f"#### Week {week_num}")
                        
                        # Create a row for each day of the week
                        days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
                        cols = st.columns(7)
                        
                        # Add day headers
                        for i, day in enumerate(days):
                            with cols[i]:
                                st.markdown(f"**{day[:3]}**")
                        
                        # Add content for each day
                        for i, day in enumerate(days):
                            with cols[i]:
                                # Find posts for this day
                                day_posts = week_data[week_data['day'] == day]
                                
                                if not day_posts.empty:
                                    for _, post in day_posts.iterrows():
                                        st.markdown(f"""
                                            <div style='padding: 10px; border-radius: 5px; background-color: #f0f2f6; margin-bottom: 5px;'>
                                                <small>{post['date'].strftime('%Y-%m-%d')}</small>
                                                <p style='margin: 5px 0; font-weight: bold;'>{post['theme']}</p>
                                                <p style='margin: 5px 0; font-size: 0.9em;'>{post['content_idea']}</p>
                                            </div>
                                        """, unsafe_allow_html=True)
                                else:
                                    st.markdown("—")
                        
                        st.markdown("---")
                
                # Export options
                st.markdown("### Export Options")
                
                col1, col2 = st.columns(2)
                
                with col1:
                    # Export as Excel
                    excel_file = export_calendar_to_excel(calendar_df)
                    st.download_button(
                        "Download Excel Calendar",
                        data=excel_file,
                        file_name=f"twitter_content_calendar_{datetime.now().strftime('%Y%m%d')}.xlsx",
                        mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                    )
                
                with col2:
                    # Export as CSV
                    csv = calendar_df.to_csv(index=False)
                    st.download_button(
                        "Download CSV Calendar",
                        data=csv,
                        file_name=f"twitter_content_calendar_{datetime.now().strftime('%Y%m%d')}.csv",
                        mime="text/csv"
                    )
        else:
            st.error("Please generate themes first")

3.2 Hashtag Strategy Manager

Implement the Hashtag Strategy Manager feature:

# lib/ai_writers/twitter_writers/content_strategy/hashtag_manager.py

import streamlit as st
import pandas as pd
import numpy as np
from typing import Dict, List, Any
import json
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
import re
import tweepy
from collections import Counter

from ....integrations.twitter.auth import create_twitter_api, create_twitter_client, is_authenticated
from ....gpt_providers.text_generation.main_text_generation import llm_text_gen

def get_trending_hashtags(location_id: int = 1) -> List[Dict[str, Any]]:
    """Get trending hashtags from Twitter API."""
    try:
        if not is_authenticated():
            return []
        
        # Get API client
        api = create_twitter_api()
        
        # Get trending topics
        trends = api.get_place_trends(location_id)
        
        # Extract hashtags
        hashtags = [
            {
                "name": trend["name"],
                "volume": trend.get("tweet_volume", 0),
                "is_hashtag": trend["name"].startswith("#")
            }
            for trend in trends[0]["trends"]
        ]
        
        # Sort by volume
        hashtags.sort(key=lambda x: x["volume"] if x["volume"] else 0, reverse=True)
        
        return hashtags
    except Exception as e:
        st.error(f"Error fetching trending hashtags: {str(e)}")
        return []

def get_trending_locations() -> List[Dict[str, Any]]:
    """Get available trending locations."""
    try:
        if not is_authenticated():
            return []
        
        # Get API client
        api = create_twitter_api()
        
        # Get available trend locations
        locations = api.available_trends()
        
        # Format locations
        formatted_locations = [
            {
                "woeid": location["woeid"],
                "name": location["name"],
                "country": location.get("countryCode", "")
            }
            for location in locations
        ]
        
        # Sort by name
        formatted_locations.sort(key=lambda x: x["name"])
        
        return formatted_locations
    except Exception as e:
        st.error(f"Error fetching trending locations: {str(e)}")
        return []

def analyze_hashtag(hashtag: str) -> Dict[str, Any]:
    """Analyze a specific hashtag."""
    try:
        if not is_authenticated():
            return {
                "volume": 0,
                "sentiment": "neutral",
                "related_hashtags": [],
                "sample_tweets": []
            }
        
        # Clean hashtag
        if not hashtag.startswith("#"):
            hashtag = f"#{hashtag}"
        
        # Get API client
        api = create_twitter_api()
        client = create_twitter_client()
        
        # Search for tweets with the hashtag
        tweets = api.search_tweets(q=hashtag, count=100, tweet_mode="extended")
        
        # Extract data
        sample_tweets = []
        all_hashtags = []
        sentiment_scores = []
        
        for tweet in tweets:
            # Skip retweets
            if hasattr(tweet, "retweeted_status"):
                continue
            
            # Add to sample tweets
            sample_tweets.append({
                "text": tweet.full_text,
                "created_at": tweet.created_at,
                "likes": tweet.favorite_count,
                "retweets": tweet.retweet_count
            })
            
            # Extract hashtags
            tweet_hashtags = [h["text"] for h in tweet.entities.get("hashtags", [])]
            all_hashtags.extend(tweet_hashtags)
            
            # Simple sentiment analysis
            text = tweet.full_text.lower()
            positive_words = ["good", "great", "awesome", "excellent", "love", "happy", "best", "amazing"]
            negative_words = ["bad", "terrible", "awful", "hate", "worst", "poor", "disappointing"]
            
            positive_count = sum(1 for word in positive_words if word in text)
            negative_count = sum(1 for word in negative_words if word in text)
            
            if positive_count > negative_count:
                sentiment_scores.append(1)  # Positive
            elif negative_count > positive_count:
                sentiment_scores.append(-1)  # Negative
            else:
                sentiment_scores.append(0)  # Neutral
        
        # Calculate related hashtags
        hashtag_counts = Counter(all_hashtags)
        related_hashtags = [
            {"name": f"#{h}", "count": count}
            for h, count in hashtag_counts.most_common(10)
            if h.lower() != hashtag[1:].lower()  # Exclude the analyzed hashtag
        ]
        
        # Calculate sentiment
        avg_sentiment = sum(sentiment_scores) / len(sentiment_scores) if sentiment_scores else 0
        sentiment = "positive" if avg_sentiment > 0.2 else "negative" if avg_sentiment < -0.2 else "neutral"
        
        # Get volume (approximate)
        volume = len(tweets)
        
        return {
            "volume": volume,
            "sentiment": sentiment,
            "related_hashtags": related_hashtags,
            "sample_tweets": sample_tweets[:5]  # Return top 5 tweets
        }
    except Exception as e:
        st.error(f"Error analyzing hashtag: {str(e)}")
        return {
            "volume": 0,
            "sentiment": "neutral",
            "related_hashtags": [],
            "sample_tweets": []
        }

def get_account_hashtags() -> Dict[str, int]:
    """Get hashtags used in the user's account."""
    try:
        if not is_authenticated():
            return {}
        
        # Get API client
        api = create_twitter_api()
        
        # Get user timeline
        tweets = api.user_timeline(count=200, tweet_mode="extended")
        
        # Extract hashtags
        all_hashtags = []
        for tweet in tweets:
            # Skip retweets
            if hasattr(tweet, "retweeted_status"):
                continue
            
            # Extract hashtags
            tweet_hashtags = [h["text"] for h in tweet.entities.get("hashtags", [])]
            all_hashtags.extend(tweet_hashtags)
        
        # Count hashtags
        hashtag_counts = Counter(all_hashtags)
        
        return dict(hashtag_counts.most_common(20))
    except Exception as e:
        st.error(f"Error getting account hashtags: {str(e)}")
        return {}

def generate_hashtag_recommendations(industry: str, topic: str) -> List[Dict[str, Any]]:
    """Generate hashtag recommendations based on industry and topic."""
    # Use AI to generate recommendations
    prompt = f"""
    Generate 10 effective Twitter hashtags for the {industry} industry, specifically for content about {topic}.
    For each hashtag, provide:
    1. The hashtag itself (including the # symbol)
    2. A brief description of why it's effective
    3. An estimate of how popular it is (high, medium, or low)
    
    Return the results as a JSON array of objects with the following structure:
    [
        {{
            "hashtag": "#example",
            "description": "Brief explanation of why this hashtag is effective",
            "popularity": "high/medium/low"
        }}
    ]
    """
    
    try:
        response = llm_text_gen(prompt)
        
        # Try to parse as JSON
        try:
            recommendations = json.loads(response)
            if isinstance(recommendations, list):
                return recommendations
        except:
            # Extract using regex
            hashtags = re.findall(r'#\w+', response)
            return [
                {
                    "hashtag": hashtag,
                    "description": f"Recommended hashtag for {topic} in {industry}",
                    "popularity": "medium"
                }
                for hashtag in hashtags[:10]
            ]
    except Exception as e:
        st.error(f"Error generating hashtag recommendations: {str(e)}")
        
        # Return generic recommendations
        return [
            {
                "hashtag": f"#{industry.lower()}",
                "description": f"Main industry hashtag",
                "popularity": "high"
            },
            {
                "hashtag": f"#{topic.lower().replace(' ', '')}",
                "description": f"Topic-specific hashtag",
                "popularity": "medium"
            }
        ]

def render_hashtag_manager():
    """Render the Hashtag Strategy Manager UI."""
    st.title("#️⃣ Hashtag Strategy Manager")
    st.markdown("Research and manage trending hashtags for better reach")
    
    # Check if connected to Twitter
    twitter_connected = is_authenticated()
    
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
    
    # Create tabs
    tab1, tab2, tab3 = st.tabs(["Trending Hashtags", "Hashtag Analysis", "Hashtag Strategy"])
    
    with tab1:
        st.markdown("### Trending Hashtags")
        
        # Get trending locations
        if twitter_connected:
            locations = get_trending_locations()
            
            if locations:
                # Create location selector
                location_options = {f"{loc['name']}, {loc['country']}": loc["woeid"] for loc in locations}
                selected_location = st.selectbox(
                    "Select Location",
                    options=list(location_options.keys()),
                    index=0
                )
                
                location_id = location_options[selected_location]
                
                # Get trending hashtags
                if st.button("Get Trending Hashtags", use_container_width=True):
                    with st.spinner("Fetching trending hashtags..."):
                        trends = get_trending_hashtags(location_id)
                        
                        if trends:
                            # Filter hashtags
                            show_all = st.checkbox("Show all trending topics (not just hashtags)")
                            
                            if not show_all:
                                trends = [t for t in trends if t["is_hashtag"]]
                            
                            # Create DataFrame
                            trends_df = pd.DataFrame(trends)
                            
                            # Display trends
                            st.dataframe(
                                trends_df,
                                column_config={
                                    "name": "Trend",
                                    "volume": st.column_config.NumberColumn("Volume", format="%d"),
                                    "is_hashtag": "Is Hashtag"
                                },
                                hide_index=True
                            )
                            
                            # Visualize top trends
                            st.markdown("#### Top Trends by Volume")
                            
                            # Filter out trends with no volume data
                            volume_trends = [t for t in trends if t["volume"]]
                            
                            if volume_trends:
                                # Sort by volume
                                volume_trends.sort(key=lambda x: x["volume"], reverse=True)
                                
                                # Take top 10
                                top_trends = volume_trends[:10]
                                
                                # Create DataFrame
                                top_df = pd.DataFrame(top_trends)
                                
                                # Create bar chart
                                fig = px.bar(
                                    top_df,
                                    x="name",
                                    y="volume",
                                    title="Top Trending Topics by Volume",
                                    labels={"name": "Trend", "volume": "Tweet Volume"}
                                )
                                st.plotly_chart(fig, use_container_width=True)
                            else:
                                st.info("No volume data available for trending topics")
                        else:
                            st.warning("No trending topics found")
            else:
                st.warning("Could not fetch trending locations")
        else:
            st.warning("Connect your Twitter account to access trending hashtags")
            
            # Show sample data
            st.markdown("### Sample Trending Hashtags")
            sample_trends = [
                {"name": "#AI", "volume": 125000, "is_hashtag": True},
                {"name": "#MachineLearning", "volume": 78000, "is_hashtag": True},
                {"name": "#DataScience", "volume": 65000, "is_hashtag": True},
                {"name": "#Python", "volume": 52000, "is_hashtag": True},
                {"name": "#BigData", "volume": 48000, "is_hashtag": True}
            ]
            
            # Create DataFrame
            sample_df = pd.DataFrame(sample_trends)
            
            # Display trends
            st.dataframe(
                sample_df,
                column_config={
                    "name": "Trend",
                    "volume": st.column_config.NumberColumn("Volume", format="%d"),
                    "is_hashtag": "Is Hashtag"
                },
                hide_index=True
            )
    
    with tab2:
        st.markdown("### Hashtag Analysis")
        
        # Hashtag input
        hashtag = st.text_input(
            "Enter a hashtag to analyze",
            placeholder="e.g., #AI"
        )
        
        if hashtag:
            # Clean hashtag
            if not hashtag.startswith("#"):
                hashtag = f"#{hashtag}"
            
            # Analyze button
            if st.button("Analyze Hashtag", use_container_width=True):
                with st.spinner(f"Analyzing {hashtag}..."):
                    if twitter_connected:
                        # Get hashtag analysis
                        analysis = analyze_hashtag(hashtag)
                        
                        # Display results
                        st.markdown(f"#### Analysis Results for {hashtag}")
                        
                        # Metrics
                        col1, col2, col3 = st.columns(3)
                        
                        with col1:
                            st.metric("Volume", f"{analysis['volume']:,}")
                        
                        with col2:
                            sentiment = analysis["sentiment"].title()
                            sentiment_color = {
                                "Positive": "green",
                                "Neutral": "blue",
                                "Negative": "red"
                            }.get(sentiment, "blue")
                            
                            st.markdown(f"""
                                <div style="text-align: center;">
                                    <p style="margin-bottom: 0;">Sentiment</p>
                                    <p style="font-size: 1.5rem; font-weight: bold; color: {sentiment_color};">{sentiment}</p>
                                </div>
                            """, unsafe_allow_html=True)
                        
                        with col3:
                            related_count = len(analysis["related_hashtags"])
                            st.metric("Related Hashtags", related_count)
                        
                        # Related hashtags
                        if analysis["related_hashtags"]:
                            st.markdown("#### Related Hashtags")
                            
                            # Create DataFrame
                            related_df = pd.DataFrame(analysis["related_hashtags"])
                            
                            # Display related hashtags
                            st.dataframe(
                                related_df,
                                column_config={
                                    "name": "Hashtag",
                                    "count": st.column_config.NumberColumn("Count", format="%d")
                                },
                                hide_index=True
                            )
                        
                        # Sample tweets
                        if analysis["sample_tweets"]:
                            st.markdown("#### Sample Tweets")
                            
                            for tweet in analysis["sample_tweets"]:
                                st.markdown(f"""
                                    <div style="padding: 10px; border-radius: 5px; background-color: #f0f2f6; margin-bottom: 10px;">
                                        <p>{tweet['text']}</p>
                                        <div style="display: flex; justify-content: space-between;">
                                            <small>{tweet['created_at'].strftime('%Y-%m-%d %H:%M')}</small>
                                            <small>❤️ {tweet['likes']} | 🔄 {tweet['retweets']}</small>
                                        </div>
                                    </div>
                                """, unsafe_allow_html=True)
                    else:
                        st.warning("Connect your Twitter account to analyze hashtags")
                        
                        # Show sample analysis
                        st.markdown(f"#### Sample Analysis for {hashtag}")
                        
                        # Metrics
                        col1, col2, col3 = st.columns(3)
                        
                        with col1:
                            st.metric("Volume", "5,280")
                        
                        with col2:
                            st.markdown("""
                                <div style="text-align: center;">
                                    <p style="margin-bottom: 0;">Sentiment</p>
                                    <p style="font-size: 1.5rem; font-weight: bold; color: green;">Positive</p>
                                </div>
                            """, unsafe_allow_html=True)
                        
                        with col3:
                            st.metric("Related Hashtags", 8)
                        
                        # Related hashtags
                        st.markdown("#### Related Hashtags")
                        
                        sample_related = [
                            {"name": "#MachineLearning", "count": 45},
                            {"name": "#DataScience", "count": 32},
                            {"name": "#Python", "count": 28},
                            {"name": "#DeepLearning", "count": 21},
                            {"name": "#BigData", "count": 18}
                        ]
                        
                        # Create DataFrame
                        related_df = pd.DataFrame(sample_related)
                        
                        # Display related hashtags
                        st.dataframe(
                            related_df,
                            column_config={
                                "name": "Hashtag",
                                "count": st.column_config.NumberColumn("Count", format="%d")
                            },
                            hide_index=True
                        )
        
        # Account hashtag analysis
        st.markdown("### Your Hashtag Usage")
        
        if twitter_connected:
            if st.button("Analyze My Hashtags", use_container_width=True):
                with st.spinner("Analyzing your hashtag usage..."):
                    # Get account hashtags
                    account_hashtags = get_account_hashtags()
                    
                    if account_hashtags:
                        # Create DataFrame
                        hashtags_df = pd.DataFrame([
                            {"hashtag": f"#{h}", "count": count}
                            for h, count in account_hashtags.items()
                        ])
                        
                        # Display hashtags
                        st.dataframe(
                            hashtags_df,
                            column_config={
                                "hashtag": "Hashtag",
                                "count": st.column_config.NumberColumn("Count", format="%d")
                            },
                            hide_index=True
                        )
                        
                        # Visualize top hashtags
                        st.markdown("#### Your Top Hashtags")
                        
                        # Create bar chart
                        fig = px.bar(
                            hashtags_df.head(10),
                            x="hashtag",
                            y="count",
                            title="Your Most Used Hashtags",
                            labels={"hashtag": "Hashtag", "count": "Usage Count"}
                        )
                        st.plotly_chart(fig, use_container_width=True)
                    else:
                        st.info("No hashtags found in your recent tweets")
        else:
            st.warning("Connect your Twitter account to analyze your hashtag usage")
    
    with tab3:
        st.markdown("### Hashtag Strategy")
        
        # Industry and topic inputs
        col1, col2 = st.columns(2)
        
        with col1:
            industry = st.selectbox(
                "Industry",
                ["Technology", "Marketing", "Education", "Health", "Finance", "Other"],
                index=0
            )
            
            if industry == "Other":
                industry = st.text_input("Specify Industry")
        
        with col2:
            topic = st.text_input(
                "Content Topic",
                placeholder="e.g., Artificial Intelligence"
            )
        
        # Generate recommendations
        if st.button("Generate Hashtag Recommendations", use_container_width=True):
            if topic:
                with st.spinner("Generating hashtag recommendations..."):
                    # Get recommendations
                    recommendations = generate_hashtag_recommendations(industry, topic)
                    
                    if recommendations:
                        # Display recommendations
                        st.markdown("#### Recommended Hashtags")
                        
                        for rec in recommendations:
                            # Determine color based on popularity
                            color = {
                                "high": "#28a745",
                                "medium": "#ffc107",
                                "low": "#6c757d"
                            }.get(rec.get("popularity", "medium").lower(), "#6c757d")
                            
                            st.markdown(f"""
                                <div style="padding: 15px; border-radius: 5px; background-color: #f8f9fa; margin-bottom: 10px; border-left: 5px solid {color};">
                                    <h4 style="margin: 0;">{rec['hashtag']}</h4>
                                    <p style="margin: 5px 0;">{rec.get('description', '')}</p>
                                    <span style="background-color: {color}; color: white; padding: 3px 8px; border-radius: 10px; font-size: 0.8em;">
                                        {rec.get('popularity', 'medium').title()} Popularity
                                    </span>
                                </div>
                            """, unsafe_allow_html=True)
                        
                        # Hashtag strategy tips
                        st.markdown("#### Hashtag Strategy Tips")
                        
                        st.markdown("""
                            - **Use a mix of popular and niche hashtags** to balance reach and competition
                            - **Limit to 2-3 hashtags per tweet** for optimal engagement
                            - **Research before using trending hashtags** to ensure relevance
                            - **Create branded hashtags** for campaigns and tracking
                            - **Place hashtags within your tweet text** when possible, rather than at the end
                            - **Monitor performance** to identify which hashtags drive the most engagement
                        """)
                        
                        # Save hashtags
                        if st.button("Save to Hashtag Library"):
                            # Store in session state
                            if "hashtag_library" not in st.session_state:
                                st.session_state.hashtag_library = []
                            
                            # Add new hashtags
                            for rec in recommendations:
                                if rec["hashtag"] not in [h["hashtag"] for h in st.session_state.hashtag_library]:
                                    st.session_state.hashtag_library.append(rec)
                            
                            st.success("Hashtags saved to your library!")
                    else:
                        st.warning("Could not generate recommendations")
            else:
                st.error("Please enter a content topic")
        
        # Hashtag library
        if "hashtag_library" in st.session_state and st.session_state.hashtag_library:
            st.markdown("### Your Hashtag Library")
            
            # Create DataFrame
            library_df = pd.DataFrame(st.session_state.hashtag_library)
            
            # Display library
            st.dataframe(
                library_df,
                column_config={
                    "hashtag": "Hashtag",
                    "description": st.column_config.TextColumn("Description", width="large"),
                    "popularity": "Popularity"
                },
                hide_index=True
            )
            
            # Export button
            if st.button("Export Hashtag Library"):
                # Convert to CSV
                csv = library_df.to_csv(index=False)
                
                # Download button
                st.download_button(
                    "Download CSV",
                    data=csv,
                    file_name=f"hashtag_library_{datetime.now().strftime('%Y%m%d')}.csv",
                    mime="text/csv"
                )

Phase 4: Visual Content Creation

4.1 Image Generator

Implement the Image Generator feature:

# lib/ai_writers/twitter_writers/visual_content/image_generator.py

import streamlit as st
import pandas as pd
import numpy as np
from typing import Dict, List, Any
import json
from datetime import datetime
import io
import base64
from PIL import Image, ImageDraw, ImageFont
import requests
import os
from pathlib import Path

# Constants
TEMPLATE_DIR = Path(__file__).parent.parent.parent.parent.parent / 'assets' / 'templates'
TEMPLATE_DIR.mkdir(exist_ok=True, parents=True)
FONT_DIR = Path(__file__).parent.parent.parent.parent.parent / 'assets' / 'fonts'
FONT_DIR.mkdir(exist_ok=True, parents=True)

def generate_quote_image(
    quote: str,
    author: str = "",
    template: str = "gradient",
    color_scheme: str = "blue"
) -> Image.Image:
    """Generate a quote image."""
    # Define templates
    templates = {
        "gradient": {
            "width": 1200,
            "height": 675,
            "background": {
                "blue": [(25, 84, 123), (142, 197, 252)],
                "green": [(25, 123, 48), (142, 252, 169)],
                "purple": [(84, 25, 123), (197, 142, 252)],
                "red": [(123, 25, 25), (252, 142, 142)]
            },
            "text_color": (255, 255, 255),
            "author_color": (255, 255, 255, 200)
        },
        "minimal": {
            "width": 1200,
            "height": 675,
            "background": {
                "blue": (240, 248, 255),
                "green": (240, 255, 240),
                "purple": (248, 240, 255),
                "red": (255, 240, 240)
            },
            "text_color": (50, 50, 50),
            "author_color": (100, 100, 100)
        },
        "bold": {
            "width": 1200,
            "height": 675,
            "background": {
                "blue": (25, 84, 123),
                "green": (25, 123, 48),
                "purple": (84, 25, 123),
                "red": (123, 25, 25)
            },
            "text_color": (255, 255, 255),
            "author_color": (255, 255, 255, 200)
        }
    }
    
    # Get template settings
    template_settings = templates.get(template, templates["gradient"])
    width = template_settings["width"]
    height = template_settings["height"]
    
    # Create image
    img = Image.new("RGB", (width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img)
    
    # Draw background
    if template == "gradient":
        # Create gradient background
        bg_colors = template_settings["background"][color_scheme]
        for y in range(height):
            r = int(bg_colors[0][0] + (bg_colors[1][0] - bg_colors[0][0]) * y / height)
            g = int(bg_colors[0][1] + (bg_colors[1][1] - bg_colors[0][1]) * y / height)
            b = int(bg_colors[0][2] + (bg_colors[1][2] - bg_colors[0][2]) * y / height)
            draw.line([(0, y), (width, y)], fill=(r, g, b))
    else:
        # Solid background
        draw.rectangle([(0, 0), (width, height)], fill=template_settings["background"][color_scheme])
    
    # Add quote
    # Try to load font, fall back to default if not available
    try:
        quote_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Bold.ttf"), 48)
        author_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Regular.ttf"), 32)
    except:
        quote_font = ImageFont.load_default()
        author_font = ImageFont.load_default()
    
    # Wrap text
    max_width = width - 200
    words = quote.split()
    lines = []
    current_line = []
    
    for word in words:
        test_line = " ".join(current_line + [word])
        bbox = draw.textbbox((0, 0), test_line, font=quote_font)
        text_width = bbox[2] - bbox[0]
        
        if text_width <= max_width:
            current_line.append(word)
        else:
            lines.append(" ".join(current_line))
            current_line = [word]
    
    if current_line:
        lines.append(" ".join(current_line))
    
    # Draw quote
    quote_text = "\n".join(lines)
    quote_bbox = draw.textbbox((0, 0), quote_text, font=quote_font)
    quote_width = quote_bbox[2] - quote_bbox[0]
    quote_height = quote_bbox[3] - quote_bbox[1]
    
    quote_x = (width - quote_width) // 2
    quote_y = (height - quote_height) // 2 - 50 if author else (height - quote_height) // 2
    
    # Add quote marks
    draw.text((quote_x - 60, quote_y - 80), """, fill=template_settings["text_color"], font=quote_font)
    
    # Draw quote text
    draw.text((quote_x, quote_y), quote_text, fill=template_settings["text_color"], font=quote_font, align="center")
    
    # Draw author
    if author:
        author_text = f"— {author}"
        author_bbox = draw.textbbox((0, 0), author_text, font=author_font)
        author_width = author_bbox[2] - author_bbox[0]
        
        author_x = (width - author_width) // 2
        author_y = quote_y + quote_height + 40
        
        draw.text((author_x, author_y), author_text, fill=template_settings["author_color"], font=author_font)
    
    return img

def generate_tweet_card(
    text: str,
    username: str = "",
    profile_image: str = None,
    theme: str = "light"
) -> Image.Image:
    """Generate a tweet card image."""
    # Define themes
    themes = {
        "light": {
            "background": (255, 255, 255),
            "text": (20, 23, 26),
            "username": (83, 100, 113),
            "border": (235, 238, 240)
        },
        "dark": {
            "background": (21, 32, 43),
            "text": (255, 255, 255),
            "username": (136, 153, 166),
            "border": (56, 68, 77)
        },
        "black": {
            "background": (0, 0, 0),
            "text": (217, 217, 217),
            "username": (110, 118, 125),
            "border": (47, 51, 54)
        }
    }
    
    # Get theme settings
    theme_settings = themes.get(theme, themes["light"])
    
    # Create image
    width = 1200
    height = 675
    img = Image.new("RGB", (width, height), color=theme_settings["background"])
    draw = ImageDraw.Draw(img)
    
    # Draw card background
    card_width = 800
    card_height = 400
    card_x = (width - card_width) // 2
    card_y = (height - card_height) // 2
    
    # Draw card border
    draw.rectangle(
        [(card_x, card_y), (card_x + card_width, card_y + card_height)],
        outline=theme_settings["border"],
        width=2
    )
    
    # Try to load font, fall back to default if not available
    try:
        text_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Regular.ttf"), 32)
        username_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Bold.ttf"), 28)
    except:
        text_font = ImageFont.load_default()
        username_font = ImageFont.load_default()
    
    # Add profile image
    profile_size = 80
    profile_x = card_x + 40
    profile_y = card_y + 40
    
    if profile_image:
        try:
            # Try to load profile image
            response = requests.get(profile_image)
            profile_img = Image.open(io.BytesIO(response.content))
            profile_img = profile_img.resize((profile_size, profile_size))
            
            # Create circular mask
            mask = Image.new("L", (profile_size, profile_size), 0)
            mask_draw = ImageDraw.Draw(mask)
            mask_draw.ellipse((0, 0, profile_size, profile_size), fill=255)
            
            # Apply mask
            profile_img.putalpha(mask)
            
            # Paste profile image
            img.paste(profile_img, (profile_x, profile_y), profile_img)
        except:
            # Draw placeholder circle
            draw.ellipse(
                [(profile_x, profile_y), (profile_x + profile_size, profile_y + profile_size)],
                fill=theme_settings["username"]
            )
    else:
        # Draw placeholder circle
        draw.ellipse(
            [(profile_x, profile_y), (profile_x + profile_size, profile_y + profile_size)],
            fill=theme_settings["username"]
        )
    
    # Add username
    if username:
        username_x = profile_x + profile_size + 20
        username_y = profile_y + 10
        
        draw.text((username_x, username_y), username, fill=theme_settings["text"], font=username_font)
        draw.text((username_x, username_y + 40), f"@{username}", fill=theme_settings["username"], font=text_font)
    
    # Wrap text
    max_width = card_width - 80
    words = text.split()
    lines = []
    current_line = []
    
    for word in words:
        test_line = " ".join(current_line + [word])
        bbox = draw.textbbox((0, 0), test_line, font=text_font)
        text_width = bbox[2] - bbox[0]
        
        if text_width <= max_width:
            current_line.append(word)
        else:
            lines.append(" ".join(current_line))
            current_line = [word]
    
    if current_line:
        lines.append(" ".join(current_line))
    
    # Draw tweet text
    tweet_text = "\n".join(lines)
    text_x = card_x + 40
    text_y = profile_y + profile_size + 40
    
    draw.text((text_x, text_y), tweet_text, fill=theme_settings["text"], font=text_font)
    
    return img

def generate_infographic(
    title: str,
    items: List[str],
    theme: str = "blue"
) -> Image.Image:
    """Generate a simple infographic."""
    # Define themes
    themes = {
        "blue": {
            "background": (240, 248, 255),
            "title_bg": (25, 84, 123),
            "title_text": (255, 255, 255),
            "item_bg": [(142, 197, 252), (173, 216, 230)],
            "item_text": (25, 25, 25)
        },
        "green": {
            "background": (240, 255, 240),
            "title_bg": (25, 123, 48),
            "title_text": (255, 255, 255),
            "item_bg": [(142, 252, 169), (173, 230, 188)],
            "item_text": (25, 25, 25)
        },
        "purple": {
            "background": (248, 240, 255),
            "title_bg": (84, 25, 123),
            "title_text": (255, 255, 255),
            "item_bg": [(197, 142, 252), (216, 173, 230)],
            "item_text": (25, 25, 25)
        },
        "red": {
            "background": (255, 240, 240),
            "title_bg": (123, 25, 25),
            "title_text": (255, 255, 255),
            "item_bg": [(252, 142, 142), (230, 173, 173)],
            "item_text": (25, 25, 25)
        }
    }
    
    # Get theme settings
    theme_settings = themes.get(theme, themes["blue"])
    
    # Create image
    width = 1200
    height = 675
    img = Image.new("RGB", (width, height), color=theme_settings["background"])
    draw = ImageDraw.Draw(img)
    
    # Try to load font, fall back to default if not available
    try:
        title_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Bold.ttf"), 48)
        item_font = ImageFont.truetype(str(FONT_DIR / "Roboto-Regular.ttf"), 32)
    except:
        title_font = ImageFont.load_default()
        item_font = ImageFont.load_default()
    
    # Draw title
    title_height = 100
    draw.rectangle([(0, 0), (width, title_height)], fill=theme_settings["title_bg"])
    
    title_bbox = draw.textbbox((0, 0), title, font=title_font)
    title_width = title_bbox[2] - title_bbox[0]
    title_x = (width - title_width) // 2
    title_y = (title_height - title_font.size) // 2
    
    draw.text((title_x, title_y), title, fill=theme_settings["title_text"], font=title_font)
    
    # Draw items
    item_height = 80
    item_padding = 20
    item_y = title_height + 50
    
    for i, item in enumerate(items):
        # Alternate background colors
        bg_color = theme_settings["item_bg"][i % 2]
        
        # Draw item background
        draw.rectangle(
            [(50, item_y), (width - 50, item_y + item_height)],
            fill=bg_color,
            outline=theme_settings["title_bg"],
            width=2
        )
        
        # Draw item number
        number_size = 60
        draw.ellipse(
            [(70, item_y + (item_height - number_size) // 2),
             (70 + number_size, item_y + (item_height - number_size) // 2 + number_size)],
            fill=theme_settings["title_bg"]
        )
        
        number_text = str(i + 1)
        number_bbox = draw.textbbox((0, 0), number_text, font=item_font)
        number_width = number_bbox[2] - number_bbox[0]
        number_height = number_bbox[3] - number_bbox[1]
        
        number_x = 70 + (number_size - number_width) // 2
        number_y = item_y + (item_height - number_height) // 2
        
        draw.text((number_x, number_y), number_text, fill=theme_settings["title_text"], font=item_font)
        
        # Draw item text
        item_text_x = 70 + number_size + 20
        item_text_y = item_y + (item_height - item_font.size) // 2
        
        draw.text((item_text_x, item_text_y), item, fill=theme_settings["item_text"], font=item_font)
        
        # Update y position for next item
        item_y += item_height + item_padding
    
    return img

def render_image_generator():
    """Render the Image Generator UI."""
    st.title("🖼️ Twitter Image Generator")
    st.markdown("Create engaging visual content for your tweets")
    
    # Create tabs for different image types
    tab1, tab2, tab3 = st.tabs(["Quote Cards", "Tweet Cards", "Infographics"])
    
    with tab1:
        st.markdown("### Quote Cards")
        st.markdown("Create shareable quote images for Twitter")
        
        # Quote input
        quote = st.text_area(
            "Quote Text",
            placeholder="Enter your quote here...",
            height=100
        )
        
        author = st.text_input(
            "Author/Source",
            placeholder="e.g., Albert Einstein"
        )
        
        # Design options
        col1, col2 = st.columns(2)
        
        with col1:
            template = st.selectbox(
                "Template",
                ["gradient", "minimal", "bold"],
                index=0
            )
        
        with col2:
            color_scheme = st.selectbox(
                "Color Scheme",
                ["blue", "green", "purple", "red"],
                index=0
            )
        
        # Generate button
        if st.button("Generate Quote Card", use_container_width=True, key="generate_quote"):
            if quote:
                with st.spinner("Generating quote card..."):
                    # Generate image
                    img = generate_quote_image(quote, author, template, color_scheme)
                    
                    # Convert to bytes
                    buf = io.BytesIO()
                    img.save(buf, format="PNG")
                    byte_im = buf.getvalue()
                    
                    # Display image
                    st.image(byte_im, caption="Generated Quote Card", use_column_width=True)
                    
                    # Download button
                    st.download_button(
                        "Download Image",
                        data=byte_im,
                        file_name=f"quote_card_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png",
                        mime="image/png"
                    )
            else:
                st.error("Please enter a quote")
    
    with tab2:
        st.markdown("### Tweet Cards")
        st.markdown("Create images that showcase your tweets")
        
        # Tweet input
        tweet_text = st.text_area(
            "Tweet Text",
            placeholder="Enter your tweet here...",
            height=100
        )
        
        username = st.text_input(
            "Twitter Username",
            placeholder="e.g., elonmusk"
        )
        
        # Design options
        theme = st.selectbox(
            "Theme",
            ["light", "dark", "black"],
            index=0
        )
        
        # Profile image
        profile_image = None
        if username:
            try:
                # Try to fetch profile image
                profile_image = f"https://unavatar.io/twitter/{username}"
            except:
                pass
        
        # Generate button
        if st.button("Generate Tweet Card", use_container_width=True, key="generate_tweet"):
            if tweet_text:
                with st.spinner("Generating tweet card..."):
                    # Generate image
                    img = generate_tweet_card(tweet_text, username, profile_image, theme)
                    
                    # Convert to bytes
                    buf = io.BytesIO()
                    img.save(buf, format="PNG")
                    byte_im = buf.getvalue()
                    
                    # Display image
                    st.image(byte_im, caption="Generated Tweet Card", use_column_width=True)
                    
                    # Download button
                    st.download_button(
                        "Download Image",
                        data=byte_im,
                        file_name=f"tweet_card_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png",
                        mime="image/png"
                    )
            else:
                st.error("Please enter tweet text")
    
    with tab3:
        st.markdown("### Infographics")
        st.markdown("Create simple infographics for Twitter")
        
        # Infographic input
        title = st.text_input(
            "Infographic Title",
            placeholder="e.g., 5 Tips for Better Tweets"
        )
        
        # Items input
        items = []
        for i in range(5):
            item = st.text_input(
                f"Item {i+1}",
                placeholder=f"Enter item {i+1}...",
                key=f"item_{i}"
            )
            if item:
                items.append(item)
        
        # Design options
        theme = st.selectbox(
            "Color Theme",
            ["blue", "green", "purple", "red"],
            index=0,
            key="infographic_theme"
        )
        
        # Generate button
        if st.button("Generate Infographic", use_container_width=True, key="generate_infographic"):
            if title and items:
                with st.spinner("Generating infographic..."):
                    # Generate image
                    img = generate_infographic(title, items, theme)
                    
                    # Convert to bytes
                    buf = io.BytesIO()
                    img.save(buf, format="PNG")
                    byte_im = buf.getvalue()
                    
                    # Display image
                    st.image(byte_im, caption="Generated Infographic", use_column_width=True)
                    
                    # Download button
                    st.download_button(
                        "Download Image",
                        data=byte_im,
                        file_name=f"infographic_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png",
                        mime="image/png"
                    )
            else:
                st.error("Please enter a title and at least one item")

Phase 5: Analytics & Optimization

5.1 Performance Analytics

Implement the Performance Analytics feature:

# lib/ai_writers/twitter_writers/analytics/performance_analytics.py

import streamlit as st
import pandas as pd
import numpy as np
from typing import Dict, List, Any
import json
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from ....integrations.twitter.auth import create_twitter_api, create_twitter_client, is_authenticated

def get_tweet_performance(days: int = 30) -> pd.DataFrame:
    """Get tweet performance data from Twitter API."""
    try:
        if not is_authenticated():
            return pd.DataFrame()
        
        # Get API client
        api = create_twitter_api()
        
        # Get user timeline
        tweets = api.user_timeline(count=200, tweet_mode="extended")
        
        # Filter by date
        cutoff_date = datetime.now() - timedelta(days=days)
        tweets = [t for t in tweets if t.created_at >= cutoff_date]
        
        # Create DataFrame
        data = []
        for tweet in tweets:
            # Skip retweets
            if hasattr(tweet, "retweeted_status"):
                continue
            
            # Extract tweet data
            tweet_data = {
                "id": tweet.id,
                "text": tweet.full_text,
                "created_at": tweet.created_at,
                "likes": tweet.favorite_count,
                "retweets": tweet.retweet_count,
                "replies": 0,  # Not available in standard API
                "impressions": 0,  # Not available in standard API
                "engagement": tweet.favorite_count + tweet.retweet_count,
                "hashtags": len(tweet.entities.get("hashtags", [])),
                "mentions": len(tweet.entities.get("user_mentions", [])),
                "urls": len(tweet.entities.get("urls", [])),
                "media": 1 if "media" in tweet.entities else 0,
                "hour": tweet.created_at.hour,
                "day": tweet.created_at.strftime("%A"),
                "length": len(tweet.full_text)
            }
            
            data.append(tweet_data)
        
        # Create DataFrame
        df = pd.DataFrame(data)
        
        return df
    except Exception as e:
        st.error(f"Error fetching tweet performance: {str(e)}")
        return pd.DataFrame()

def analyze_performance(df: pd.DataFrame) -> Dict[str, Any]:
    """Analyze tweet performance data."""
    if df.empty:
        return {
            "total_engagement": 0,
            "avg_engagement": 0,
            "top_tweet": None,
            "worst_tweet": None,
            "best_time": None,
            "best_day": None,
            "media_impact": 0,
            "hashtag_impact": 0,
            "length_impact": 0
        }
    
    # Calculate metrics
    total_engagement = df["engagement"].sum()
    avg_engagement = df["engagement"].mean()
    
    # Find top and worst tweets
    top_tweet = df.loc[df["engagement"].idxmax()] if not df.empty else None
    worst_tweet = df.loc[df["engagement"].idxmin()] if not df.empty else None
    
    # Find best time and day
    hour_engagement = df.groupby("hour")["engagement"].mean()
    best_hour = hour_engagement.idxmax() if not hour_engagement.empty else None
    
    day_engagement = df.groupby("day")["engagement"].mean()
    best_day = day_engagement.idxmax() if not day_engagement.empty else None
    
    # Calculate impact factors
    media_impact = df[df["media"] == 1]["engagement"].mean() / avg_engagement if avg_engagement > 0 else 1
    
    hashtag_impact = 0
    if not df.empty:
        hashtag_groups = df.groupby("hashtags")["engagement"].mean()
        optimal_hashtags = hashtag_groups.idxmax() if not hashtag_groups.empty else 0
        hashtag_impact = hashtag_groups.max() / avg_engagement if avg_engagement > 0 else 1
    
    length_impact = 0
    if not df.empty:
        df["length_bin"] = pd.cut(df["length"], bins=[0, 70, 140, 210, 280], labels=["0-70", "71-140", "141-210", "211-280"])
        length_groups = df.groupby("length_bin")["engagement"].mean()
        optimal_length = length_groups.idxmax() if not length_groups.empty else "71-140"
        length_impact = length_groups.max() / avg_engagement if avg_engagement > 0 else 1
    
    return {
        "total_engagement": total_engagement,
        "avg_engagement": avg_engagement,
        "top_tweet": top_tweet,
        "worst_tweet": worst_tweet,
        "best_time": best_hour,
        "best_day": best_day,
        "media_impact": media_impact,
        "hashtag_impact": hashtag_impact,
        "length_impact": length_impact
    }

def render_performance_analytics():
    """Render the Performance Analytics UI."""
    st.title("📊 Twitter Performance Analytics")
    st.markdown("Track tweet performance and engagement metrics")
    
    # Check if connected to Twitter
    twitter_connected = is_authenticated()
    
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
        
        # Date range selector
        col1, col2 = st.columns(2)
        
        with col1:
            days = st.slider(
                "Time Period",
                min_value=7,
                max_value=90,
                value=30,
                step=1
            )
        
        with col2:
            refresh = st.button("Refresh Data", use_container_width=True)
        
        # Get performance data
        with st.spinner("Fetching tweet performance data..."):
            df = get_tweet_performance(days)
            
            if not df.empty:
                # Store in session state
                st.session_state.tweet_performance = df
                
                # Analyze performance
                analysis = analyze_performance(df)
                
                # Display overview metrics
                st.markdown("### Performance Overview")
                
                col1, col2, col3, col4 = st.columns(4)
                
                with col1:
                    st.metric("Total Tweets", len(df))
                
                with col2:
                    st.metric("Total Engagement", f"{analysis['total_engagement']:,}")
                
                with col3:
                    st.metric("Avg. Engagement", f"{analysis['avg_engagement']:.1f}")
                
                with col4:
                    st.metric("Media Impact", f"{analysis['media_impact']:.2f}x")
                
                # Engagement over time
                st.markdown("### Engagement Over Time")
                
                # Sort by date
                df_sorted = df.sort_values("created_at")
                
                # Create time series chart
                fig = px.line(
                    df_sorted,
                    x="created_at",
                    y="engagement",
                    title="Tweet Engagement History",
                    labels={"created_at": "Date", "engagement": "Engagement"}
                )
                
                # Add 7-day moving average
                df_sorted["ma7"] = df_sorted["engagement"].rolling(7).mean()
                
                fig.add_trace(
                    go.Scatter(
                        x=df_sorted["created_at"],
                        y=df_sorted["ma7"],
                        mode="lines",
                        name="7-day Moving Average",
                        line=dict(color="red", width=2)
                    )
                )
                
                st.plotly_chart(fig, use_container_width=True)
                
                # Engagement by day and hour
                st.markdown("### Engagement by Day and Hour")
                
                # Create heatmap
                pivot = df.pivot_table(
                    index="day",
                    columns="hour",
                    values="engagement",
                    aggfunc="mean"
                )
                
                # Reorder days
                days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
                pivot = pivot.reindex(days_order)
                
                fig = px.imshow(
                    pivot,
                    labels=dict(x="Hour of Day", y="Day of Week", color="Avg. Engagement"),
                    x=list(range(24)),
                    y=days_order,
                    color_continuous_scale="Viridis",
                    title="Engagement Heatmap by Day and Hour"
                )
                
                st.plotly_chart(fig, use_container_width=True)
                
                # Content analysis
                st.markdown("### Content Analysis")
                
                col1, col2 = st.columns(2)
                
                with col1:
                    # Engagement by tweet length
                    df["length_bin"] = pd.cut(df["length"], bins=[0, 70, 140, 210, 280], labels=["0-70", "71-140", "141-210", "211-280"])
                    length_engagement = df.groupby("length_bin")["engagement"].mean().reset_index()
                    
                    fig = px.bar(
                        length_engagement,
                        x="length_bin",
                        y="engagement",
                        title="Engagement by Tweet Length",
                        labels={"length_bin": "Character Count", "engagement": "Avg. Engagement"}
                    )
                    st.plotly_chart(fig, use_container_width=True)
                
                with col2:
                    # Engagement by hashtag count
                    hashtag_engagement = df.groupby("hashtags")["engagement"].mean().reset_index()
                    
                    fig = px.bar(
                        hashtag_engagement,
                        x="hashtags",
                        y="engagement",
                        title="Engagement by Hashtag Count",
                        labels={"hashtags": "Number of Hashtags", "engagement": "Avg. Engagement"}
                    )
                    st.plotly_chart(fig, use_container_width=True)
                
                # Media impact
                st.markdown("### Media Impact")
                
                media_engagement = df.groupby("media")["engagement"].mean().reset_index()
                media_engagement["media"] = media_engagement["media"].map({0: "No Media", 1: "With Media"})
                
                fig = px.bar(
                    media_engagement,
                    x="media",
                    y="engagement",
                    title="Engagement by Media Inclusion",
                    labels={"media": "Media", "engagement": "Avg. Engagement"},
                    color="media",
                    color_discrete_map={"No Media": "#1DA1F2", "With Media": "#17BF63"}
                )
                st.plotly_chart(fig, use_container_width=True)
                
                # Top tweets
                st.markdown("### Top Performing Tweets")
                
                # Sort by engagement
                top_tweets = df.sort_values("engagement", ascending=False).head(5)
                
                for i, (_, tweet) in enumerate(top_tweets.iterrows()):
                    st.markdown(f"""
                        <div style="padding: 15px; border-radius: 5px; background-color: #f0f2f6; margin-bottom: 15px;">
                            <p style="margin: 0 0 10px 0;">{tweet['text']}</p>
                            <div style="display: flex; justify-content: space-between;">
                                <small>{tweet['created_at'].strftime('%Y-%m-%d %H:%M')}</small>
                                <small>❤️ {tweet['likes']} | 🔄 {tweet['retweets']} | 💬 {tweet['replies']}</small>
                            </div>
                        </div>
                    """, unsafe_allow_html=True)
                
                # Recommendations
                st.markdown("### Performance Recommendations")
                
                recommendations = []
                
                # Best time to post
                if analysis["best_time"] is not None and analysis["best_day"] is not None:
                    recommendations.append(f"Post on {analysis['best_day']} around {analysis['best_time']}:00 for optimal engagement")
                
                # Media recommendation
                if analysis["media_impact"] > 1.2:
                    recommendations.append(f"Including media increases engagement by {(analysis['media_impact']-1)*100:.1f}%")
                
                # Hashtag recommendation
                hashtag_groups = df.groupby("hashtags")["engagement"].mean()
                if not hashtag_groups.empty:
                    optimal_hashtags = hashtag_groups.idxmax()
                    recommendations.append(f"Using {optimal_hashtags} hashtags yields the best engagement")
                
                # Length recommendation
                length_groups = df.groupby("length_bin")["engagement"].mean()
                if not length_groups.empty:
                    optimal_length = length_groups.idxmax()
                    recommendations.append(f"Tweets with {optimal_length} characters perform best")
                
                # Display recommendations
                for rec in recommendations:
                    st.info(rec)
                
                # Export options
                st.markdown("### Export Data")
                
                col1, col2 = st.columns(2)
                
                with col1:
                    # Export as CSV
                    csv = df.to_csv(index=False)
                    st.download_button(
                        "Download CSV",
                        data=csv,
                        file_name=f"tweet_performance_{datetime.now().strftime('%Y%m%d')}.csv",
                        mime="text/csv"
                    )
                
                with col2:
                    # Export as JSON
                    json_str = df.to_json(orient="records", date_format="iso")
                    st.download_button(
                        "Download JSON",
                        data=json_str,
                        file_name=f"tweet_performance_{datetime.now().strftime('%Y%m%d')}.json",
                        mime="application/json"
                    )
            else:
                st.warning("No tweets found in the selected time period")
    else:
        st.warning("Connect your Twitter account to access performance analytics")
        
        # Show sample data
        st.markdown("### Sample Performance Data")
        
        # Create sample data
        dates = pd.date_range(start=datetime.now() - timedelta(days=30), periods=30)
        engagements = np.random.normal(loc=20, scale=10, size=30)
        engagements = np.abs(engagements)  # Make all values positive
        
        df = pd.DataFrame({
            "date": dates,
            "engagement": engagements
        })
        
        # Sample metrics
        col1, col2, col3, col4 = st.columns(4)
        
        with col1:
            st.metric("Total Tweets", "42")
        
        with col2:
            st.metric("Total Engagement", "1,254")
        
        with col3:
            st.metric("Avg. Engagement", "29.8")
        
        with col4:
            st.metric("Media Impact", "1.45x")
        
        # Sample chart
        fig = px.line(
            df,
            x="date",
            y="engagement",
            title="Sample Tweet Engagement History"
        )
        st.plotly_chart(fig, use_container_width=True)

Phase 6: Integration and Dashboard Updates

6.1 Update Twitter Dashboard

Update the Twitter dashboard to include the new features:

# lib/ai_writers/twitter_writers/twitter_dashboard.py

import streamlit as st
import streamlit.components.v1 as components
from typing import Dict, List
import json
from datetime import datetime

from .tweet_generator.enhanced_tweet_generator import enhanced_tweet_generator
from .tweet_performance.performance_predictor import render_performance_predictor
from .content_strategy.calendar_generator import render_calendar_generator
from .content_strategy.hashtag_manager import render_hashtag_manager
from .visual_content.image_generator import render_image_generator
from .analytics.performance_analytics import render_performance_analytics
from ...integrations.twitter.auth import is_authenticated
from ...integrations.twitter.account_manager import render_twitter_account_manager
from ...integrations.twitter.api_key_manager import render_twitter_api_key_manager
from ...integrations.twitter.callback_handler import handle_oauth_callback

def load_feature_data() -> Dict:
    """Load feature data from a structured format."""
    return {
        "tweet_generation": {
            "title": "Tweet Generation & Optimization",
            "icon": "🐦",
            "description": "Create and optimize engaging tweets with AI assistance",
            "features": [
                {
                    "name": "Smart Tweet Generator",
                    "description": "Generate multiple tweet variations with optimal character count, hashtags, and emojis",
                    "status": "active",
                    "icon": "✨",
                    "function": enhanced_tweet_generator
                },
                {
                    "name": "Tweet Performance Predictor",
                    "description": "Predict engagement rates and best posting times for maximum impact",
                    "status": "active",
                    "icon": "📊",
                    "function": render_performance_predictor
                }
            ]
        },
        "content_strategy": {
            "title": "Content Strategy Tools",
            "icon": "📅",
            "description": "Plan and manage your Twitter content strategy effectively",
            "features": [
                {
                    "name": "Content Calendar Generator",
                    "description": "Create weekly/monthly content plans with theme-based scheduling",
                    "status": "active",
                    "icon": "🗓️",
                    "function": render_calendar_generator
                },
                {
                    "name": "Hashtag Strategy Manager",
                    "description": "Research and manage trending hashtags for better reach",
                    "status": "active",
                    "icon": "#️⃣",
                    "function": render_hashtag_manager
                }
            ]
        },
        "visual_content": {
            "title": "Visual Content Creation",
            "icon": "🎨",
            "description": "Create engaging visual content for your tweets",
            "features": [
                {
                    "name": "Image Generator",
                    "description": "Create tweet cards, infographics, and quote designs",
                    "status": "active",
                    "icon": "🖼️",
                    "function": render_image_generator
                },
                {
                    "name": "Video Content Assistant",
                    "description": "Generate video scripts and optimize captions",
                    "status": "coming_soon",
                    "icon": "🎥"
                }
            ]
        },
        "engagement": {
            "title": "Engagement & Community",
            "icon": "🤝",
            "description": "Manage and enhance community engagement",
            "features": [
                {
                    "name": "Reply Generator",
                    "description": "Generate context-aware responses with appropriate tone",
                    "status": "coming_soon",
                    "icon": "💬"
                },
                {
                    "name": "Community Tools",
                    "description": "Create polls and plan Q&A sessions",
                    "status": "coming_soon",
                    "icon": "👥"
                }
            ]
        },
        "analytics": {
            "title": "Analytics & Optimization",
            "icon": "📈",
            "description": "Track performance and optimize your Twitter strategy",
            "features": [
                {
                    "name": "Performance Analytics",
                    "description": "Track tweet performance and engagement metrics",
                    "status": "active",
                    "icon": "📊",
                    "function": render_performance_analytics
                },
                {
                    "name": "A/B Testing Assistant",
                    "description": "Test and optimize tweet variations for better results",
                    "status": "coming_soon",
                    "icon": "🔍"
                }
            ]
        },
        "research": {
            "title": "Research & Intelligence",
            "icon": "🔎",
            "description": "Gain insights and stay ahead of trends",
            "features": [
                {
                    "name": "Market Research",
                    "description": "Analyze competitors and track industry trends",
                    "status": "coming_soon",
                    "icon": "📊"
                },
                {
                    "name": "Content Inspiration",
                    "description": "Get trending topic suggestions and content ideas",
                    "status": "coming_soon",
                    "icon": "💡"
                }
            ]
        }
    }

def render_feature_card(feature: Dict) -> None:
    """Render a single feature card with its details."""
    with st.container():
        st.markdown(f"""
            <div style='padding: 20px; border-radius: 10px; background-color: #f0f2f6; margin-bottom: 20px;'>
                <h3 style='margin: 0;'>{feature['icon']} {feature['name']}</h3>
                <p style='margin: 10px 0;'>{feature['description']}</p>
                <span style='background-color: {'#4CAF50' if feature['status'] == 'active' else '#ffd700'}; 
                            padding: 5px 10px; border-radius: 15px; font-size: 0.8em;'>
                    {feature['status'].title()}
                </span>
            </div>
        """, unsafe_allow_html=True)

def render_category_section(category: Dict) -> None:
    """Render a category section with all its features."""
    st.markdown(f"### {category['icon']} {category['title']}")
    st.markdown(f"*{category['description']}*")
    
    col1, col2 = st.columns(2)
    with col1:
        render_feature_card(category['features'][0])
        if category['features'][0]['status'] == 'active':
            if st.button(f"Launch {category['features'][0]['name']}", key=f"launch_{category['features'][0]['name']}"):
                st.session_state.current_feature = category['features'][0]['function']
                st.experimental_rerun()
    with col2:
        render_feature_card(category['features'][1])
        if category['features'][1]['status'] == 'active':
            if st.button(f"Launch {category['features'][1]['name']}", key=f"launch_{category['features'][1]['name']}"):
                st.session_state.current_feature = category['features'][1]['function']
                st.experimental_rerun()

def run_dashboard():
    """Main function to run the Twitter dashboard."""
    # Handle OAuth callback if present
    handle_oauth_callback()
    
    # Check if a feature is currently active
    if "current_feature" in st.session_state and callable(st.session_state.current_feature):
        # Add back button
        if st.button("← Back to Dashboard"):
            del st.session_state.current_feature
            st.experimental_rerun()
        
        # Run the current feature
        st.session_state.current_feature()
        return
    
    # Header
    st.title("🐦 Twitter AI Writer Dashboard")
    st.markdown("""
        Welcome to your all-in-one Twitter content creation and management platform. 
        Explore our AI-powered tools to enhance your Twitter marketing strategy.
    """)
    
    # Twitter account status
    twitter_connected = is_authenticated()
    
    if twitter_connected:
        user = st.session_state.twitter_user
        st.success(f"Connected as @{user['screen_name']}")
    else:
        st.warning("Connect your Twitter account to access all features")
        
        # Show account connection UI
        with st.expander("Twitter Account Setup", expanded=True):
            # First show API key manager
            render_twitter_api_key_manager()
            
            # Then show account manager
            render_twitter_account_manager()
    
    # Create tabs for different sections
    tab1, tab2, tab3 = st.tabs(["🎯 Quick Actions", "📊 Analytics", "⚙️ Settings"])
    
    with tab1:
        st.markdown("### 🚀 Quick Actions")
        col1, col2, col3 = st.columns(3)
        
        with col1:
            if st.button("📝 Create New Tweet", use_container_width=True):
                # Set the current feature to the enhanced tweet generator
                st.session_state.current_feature = enhanced_tweet_generator
                st.experimental_rerun()
        with col2:
            if st.button("📅 Plan Content", use_container_width=True):
                # Set the current feature to the calendar generator
                st.session_state.current_feature = render_calendar_generator
                st.experimental_rerun()
        with col3:
            if st.button("📊 View Analytics", use_container_width=True):
                # Set the current feature to performance analytics
                st.session_state.current_feature = render_performance_analytics
                st.experimental_rerun()
    
    with tab2:
        st.markdown("### 📈 Analytics Dashboard")
        
        if twitter_connected:
            # Show mini analytics dashboard
            try:
                from .analytics.performance_analytics import get_tweet_performance, analyze_performance
                
                # Get performance data
                with st.spinner("Loading analytics..."):
                    df = get_tweet_performance(days=30)
                    
                    if not df.empty:
                        # Analyze performance
                        analysis = analyze_performance(df)
                        
                        # Display overview metrics
                        col1, col2, col3, col4 = st.columns(4)
                        
                        with col1:
                            st.metric("Total Tweets", len(df))
                        
                        with col2:
                            st.metric("Total Engagement", f"{analysis['total_engagement']:,}")
                        
                        with col3:
                            st.metric("Avg. Engagement", f"{analysis['avg_engagement']:.1f}")
                        
                        with col4:
                            st.metric("Media Impact", f"{analysis['media_impact']:.2f}x")
                        
                        # Show engagement chart
                        import plotly.express as px
                        
                        # Sort by date
                        df_sorted = df.sort_values("created_at")
                        
                        # Create time series chart
                        fig = px.line(
                            df_sorted,
                            x="created_at",
                            y="engagement",
                            title="Recent Tweet Engagement",
                            labels={"created_at": "Date", "engagement": "Engagement"}
                        )
                        
                        st.plotly_chart(fig, use_container_width=True)
                        
                        # View full analytics button
                        if st.button("View Full Analytics", use_container_width=True):
                            st.session_state.current_feature = render_performance_analytics
                            st.experimental_rerun()
                    else:
                        st.info("No recent tweets found. Start tweeting to see analytics!")
            except Exception as e:
                st.error(f"Error loading analytics: {str(e)}")
                st.info("View full analytics for detailed insights")
        else:
            st.info("Connect your Twitter account to view analytics")
    
    with tab3:
        st.markdown("### ⚙️ Settings")
        
        # Twitter account settings
        with st.expander("Twitter Account", expanded=True):
            render_twitter_account_manager()
        
        # API key settings
        with st.expander("API Configuration", expanded=False):
            render_twitter_api_key_manager()
        
        # Preferences
        with st.expander("Preferences", expanded=False):
            st.markdown("#### Dashboard Preferences")
            
            # Theme preference
            theme = st.selectbox(
                "Theme",
                ["Light", "Dark", "System"],
                index=2
            )
            
            # Default content type
            content_type = st.selectbox(
                "Default Content Type",
                ["Informative", "Promotional", "Engaging", "Educational"],
                index=0
            )
            
            # Save preferences
            if st.button("Save Preferences"):
                st.session_state.twitter_preferences = {
                    "theme": theme,
                    "content_type": content_type
                }
                st.success("Preferences saved!")
    
    # Load feature data
    features = load_feature_data()
    
    # Main content area
    st.markdown("## 🛠️ Available Tools")
    
    # Render each category
    for category in features.values():
        render_category_section(category)
    
    # Footer
    st.markdown("---")
    st.markdown("""
        <div style='text-align: center;'>
            <p>Need help? Check out our <a href='#'>documentation</a> or <a href='#'>contact support</a></p>
        </div>
    """, unsafe_allow_html=True)

if __name__ == "__main__":
    run_dashboard()

Testing Plan

Unit Testing

  1. Authentication Tests

    • Test Twitter API key validation
    • Test OAuth flow
    • Test token storage and retrieval
  2. API Integration Tests

    • Test tweet posting
    • Test timeline retrieval
    • Test hashtag search
    • Test media upload
  3. Feature Tests

    • Test tweet generation
    • Test performance prediction
    • Test content calendar creation
    • Test image generation

Integration Testing

  1. End-to-End Flow Tests

    • Test complete user journey from authentication to posting
    • Test data flow between components
    • Test error handling and recovery
  2. Cross-Feature Tests

    • Test integration between tweet generator and performance predictor
    • Test integration between content calendar and tweet scheduler
    • Test integration between analytics and content recommendations

User Acceptance Testing

  1. Usability Tests

    • Test with real users to gather feedback
    • Evaluate UI/UX design
    • Measure time to complete common tasks
  2. Performance Tests

    • Test with large datasets
    • Measure response times
    • Identify bottlenecks

Implementation Timeline

Phase 1: Twitter Authentication & Basic Integration (2 weeks)

  • Week 1: Set up Twitter API authentication system
  • Week 2: Create account connection UI and API key management

Phase 2: Enhanced Tweet Generator (2 weeks)

  • Week 3: Implement real Twitter data integration
  • Week 4: Develop tweet performance predictor

Phase 3: Content Strategy Tools (3 weeks)

  • Week 5: Implement content calendar generator
  • Week 6-7: Develop hashtag strategy manager

Phase 4: Visual Content Creation (2 weeks)

  • Week 8-9: Implement image generator for quotes, tweets, and infographics

Phase 5: Analytics & Optimization (2 weeks)

  • Week 10-11: Implement performance analytics dashboard

Phase 6: Integration and Dashboard Updates (1 week)

  • Week 12: Update Twitter dashboard and integrate all features

Conclusion

This implementation plan provides a comprehensive approach to enhancing the Twitter features in AI-Writer. By leveraging the Tweepy library and user-provided API keys, we can transform the "coming soon" features into fully functional components that provide real value to users.

The phased approach allows for incremental delivery of features, with each phase building on the previous one. This ensures that users can start benefiting from the enhancements early, while more advanced features are developed.

Key benefits of this implementation:

  1. Real Twitter Integration: Users can connect their Twitter accounts and interact directly with the platform.
  2. Data-Driven Insights: Performance analytics and predictions based on real Twitter data.
  3. Comprehensive Content Strategy: Tools for planning, creating, and optimizing Twitter content.
  4. Visual Content Creation: Easy-to-use tools for creating engaging visual content.
  5. Streamlined Workflow: Integrated dashboard for managing all Twitter activities.

By following this plan, AI-Writer will stand out against competitors by offering a complete Twitter content creation and management solution that leverages real data and AI to optimize performance.