this repo has no description
40
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make X profile search configurable for performance vs depth

- Added include_timeline parameter (default: True)
- Full mode: Fetches 10 recent posts via separate API call (comprehensive)
- Fast mode: Uses expansions to get pinned + most recent tweet only (1 API call)
- Output includes mode indicator for transparency
- Gives void flexibility to choose between thorough analysis or quick lookups

This allows optimizing API usage based on context - full timeline when analyzing someone deeply, fast mode for quick reference checks.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

+122 -6
+2 -2
register_x_tools.py
··· 133 133 { 134 134 "func": search_x_profile, 135 135 "args_schema": SearchXProfileArgs, 136 - "description": "Look up detailed profile information for an X (Twitter) user", 137 - "tags": ["x", "twitter", "profile", "lookup", "user"] 136 + "description": "Look up detailed profile information for an X (Twitter) user including recent posts and conversations", 137 + "tags": ["x", "twitter", "profile", "lookup", "user", "posts", "activity"] 138 138 } 139 139 ] 140 140
+120 -4
tools/x_profile.py
··· 5 5 6 6 class SearchXProfileArgs(BaseModel): 7 7 username: str = Field(..., description="X username to look up (without @)") 8 + include_timeline: bool = Field(default=True, description="Include full timeline (10 posts) vs just pinned/most recent tweet") 8 9 9 10 10 - def search_x_profile(username: str) -> str: 11 + def search_x_profile(username: str, include_timeline: bool = True) -> str: 11 12 """ 12 - Look up detailed profile information for an X (Twitter) user. 13 + Look up detailed profile information for an X (Twitter) user, including recent activity. 13 14 14 15 Args: 15 16 username: X username to look up (without @) 17 + include_timeline: If True, fetches full timeline (10 posts). If False, only pinned/most recent tweet. 16 18 17 19 Returns: 18 - YAML-formatted profile information including bio, metrics, verification status, etc. 20 + YAML-formatted profile information including bio, metrics, verification status, and recent posts/replies. 19 21 """ 20 22 import os 21 23 import yaml ··· 68 70 "public_metrics", 69 71 "profile_image_url", 70 72 "profile_banner_url", 71 - "protected" 73 + "protected", 74 + "pinned_tweet_id", 75 + "most_recent_tweet_id" 72 76 ]) 73 77 } 74 78 79 + # Add expansions if not fetching full timeline 80 + if not include_timeline: 81 + user_params["expansions"] = "pinned_tweet_id,most_recent_tweet_id" 82 + user_params["tweet.fields"] = "id,text,created_at,referenced_tweets,conversation_id" 83 + 75 84 try: 76 85 response = requests.get(user_lookup_url, headers=headers, params=user_params, timeout=10) 77 86 response.raise_for_status() ··· 127 136 "listed_count": metrics.get("listed_count", 0), 128 137 "like_count": metrics.get("like_count", 0) 129 138 } 139 + 140 + # Handle recent activity based on include_timeline parameter 141 + user_id = user.get("id") 142 + if user_id and not user.get("protected", False): # Only fetch if account is public 143 + recent_posts = [] 144 + 145 + if include_timeline: 146 + # Full timeline mode: Fetch recent tweets with separate API call 147 + try: 148 + tweets_url = f"{base_url}/users/{user_id}/tweets" 149 + tweets_params = { 150 + "max_results": 10, # Get last 10 posts/replies 151 + "tweet.fields": "id,text,created_at,referenced_tweets,conversation_id,in_reply_to_user_id", 152 + # Don't exclude replies - we want to see conversations 153 + } 154 + 155 + tweets_response = requests.get(tweets_url, headers=headers, params=tweets_params, timeout=10) 156 + tweets_response.raise_for_status() 157 + tweets_data = tweets_response.json() 158 + 159 + # Format recent activity 160 + for tweet in tweets_data.get("data", [])[:10]: # Limit to 10 most recent 161 + # Determine tweet type 162 + tweet_type = "tweet" 163 + referenced_tweets = tweet.get("referenced_tweets", []) 164 + for ref in referenced_tweets: 165 + if ref.get("type") == "retweeted": 166 + tweet_type = "retweet" 167 + break 168 + elif ref.get("type") == "replied_to": 169 + tweet_type = "reply" 170 + break 171 + elif ref.get("type") == "quoted": 172 + tweet_type = "quote" 173 + break 174 + 175 + post_data = { 176 + "text": tweet.get("text", ""), 177 + "created_at": tweet.get("created_at", ""), 178 + "type": tweet_type, 179 + "url": f"https://x.com/{username}/status/{tweet.get('id', '')}" 180 + } 181 + 182 + # Add conversation context for replies 183 + if tweet_type == "reply" and tweet.get("conversation_id"): 184 + post_data["conversation_id"] = tweet.get("conversation_id") 185 + if tweet.get("in_reply_to_user_id"): 186 + post_data["replying_to_user_id"] = tweet.get("in_reply_to_user_id") 187 + 188 + recent_posts.append(post_data) 189 + 190 + except Exception as e: 191 + # Log error but don't fail the entire profile lookup 192 + profile_data["x_user_profile"]["recent_activity"] = { 193 + "error": f"Could not fetch recent posts: {str(e)}" 194 + } 195 + else: 196 + # Fast mode: Use expanded tweets from user lookup response 197 + if "includes" in data and "tweets" in data["includes"]: 198 + expanded_tweets = data["includes"]["tweets"] 199 + 200 + # Process pinned tweet if present 201 + pinned_tweet_id = user.get("pinned_tweet_id") 202 + if pinned_tweet_id: 203 + for tweet in expanded_tweets: 204 + if tweet.get("id") == pinned_tweet_id: 205 + post_data = { 206 + "text": tweet.get("text", ""), 207 + "created_at": tweet.get("created_at", ""), 208 + "type": "pinned", 209 + "url": f"https://x.com/{username}/status/{tweet.get('id', '')}" 210 + } 211 + recent_posts.append(post_data) 212 + break 213 + 214 + # Process most recent tweet if present and different from pinned 215 + recent_tweet_id = user.get("most_recent_tweet_id") 216 + if recent_tweet_id and recent_tweet_id != pinned_tweet_id: 217 + for tweet in expanded_tweets: 218 + if tweet.get("id") == recent_tweet_id: 219 + # Determine tweet type 220 + tweet_type = "tweet" 221 + referenced_tweets = tweet.get("referenced_tweets", []) 222 + for ref in referenced_tweets: 223 + if ref.get("type") == "replied_to": 224 + tweet_type = "reply" 225 + break 226 + elif ref.get("type") == "quoted": 227 + tweet_type = "quote" 228 + break 229 + 230 + post_data = { 231 + "text": tweet.get("text", ""), 232 + "created_at": tweet.get("created_at", ""), 233 + "type": tweet_type, 234 + "url": f"https://x.com/{username}/status/{tweet.get('id', '')}" 235 + } 236 + recent_posts.append(post_data) 237 + break 238 + 239 + # Add recent activity to profile data if we have posts 240 + if recent_posts: 241 + profile_data["x_user_profile"]["recent_activity"] = { 242 + "post_count": len(recent_posts), 243 + "mode": "full_timeline" if include_timeline else "expanded_only", 244 + "posts": recent_posts 245 + } 130 246 131 247 return yaml.dump(profile_data, default_flow_style=False, sort_keys=False) 132 248