# 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: ```python # 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: ```python # 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: ```python # 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"""

{tweet.text}

Posted on {tweet.created_at.strftime('%Y-%m-%d %H:%M')}
""", 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("""
Connect Twitter Account
""".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: ```python # 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: ```python # 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"""

Tweet Variation {i + 1}

{tweet['text']}

Score: {tweet['engagement_score']}% {count_characters(tweet['text'])}/280 chars
""", 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: ```python # 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: ```python # 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"""
{post['date'].strftime('%Y-%m-%d')}

{post['theme']}

{post['content_idea']}

""", 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: ```python # 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"""

Sentiment

{sentiment}

""", 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"""

{tweet['text']}

{tweet['created_at'].strftime('%Y-%m-%d %H:%M')} âĪïļ {tweet['likes']} | 🔄 {tweet['retweets']}
""", 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("""

Sentiment

Positive

""", 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"""

{rec['hashtag']}

{rec.get('description', '')}

{rec.get('popularity', 'medium').title()} Popularity
""", 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: ```python # 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: ```python # 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"""

{tweet['text']}

{tweet['created_at'].strftime('%Y-%m-%d %H:%M')} âĪïļ {tweet['likes']} | 🔄 {tweet['retweets']} | 💎 {tweet['replies']}
""", 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: ```python # 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"""

{feature['icon']} {feature['name']}

{feature['description']}

{feature['status'].title()}
""", 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("""

Need help? Check out our documentation or contact support

""", 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.