Fix Bird CLI response parsing
1. last30days.py: Check isinstance(dict) before .get() - Bird returns list on success, dict on error 2. bird_x.py: Update field mappings for Bird's actual response format: - author.username not user.screen_name - createdAt not created_at (camelCase) - likeCount, retweetCount, etc. (camelCase) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -158,8 +158,8 @@ def _search_x(
|
|||||||
|
|
||||||
x_items = bird_x.parse_bird_response(raw_response or {})
|
x_items = bird_x.parse_bird_response(raw_response or {})
|
||||||
|
|
||||||
# Check for error in response
|
# Check for error in response (Bird returns list on success, dict on error)
|
||||||
if raw_response and raw_response.get("error") and not x_error:
|
if raw_response and isinstance(raw_response, dict) and raw_response.get("error") and not x_error:
|
||||||
x_error = raw_response["error"]
|
x_error = raw_response["error"]
|
||||||
|
|
||||||
return x_items, raw_response, x_error
|
return x_items, raw_response, x_error
|
||||||
|
|||||||
+13
-11
@@ -198,16 +198,18 @@ def parse_bird_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|||||||
# Extract URL - Bird uses permanent_url or we construct from id
|
# Extract URL - Bird uses permanent_url or we construct from id
|
||||||
url = tweet.get("permanent_url") or tweet.get("url", "")
|
url = tweet.get("permanent_url") or tweet.get("url", "")
|
||||||
if not url and tweet.get("id"):
|
if not url and tweet.get("id"):
|
||||||
screen_name = tweet.get("user", {}).get("screen_name", "")
|
# Try different field structures Bird might use
|
||||||
|
author = tweet.get("author", {}) or tweet.get("user", {})
|
||||||
|
screen_name = author.get("username") or author.get("screen_name", "")
|
||||||
if screen_name:
|
if screen_name:
|
||||||
url = f"https://x.com/{screen_name}/status/{tweet['id']}"
|
url = f"https://x.com/{screen_name}/status/{tweet['id']}"
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Parse date from created_at (e.g., "Wed Jan 15 14:30:00 +0000 2026")
|
# Parse date from created_at/createdAt (e.g., "Wed Jan 15 14:30:00 +0000 2026")
|
||||||
date = None
|
date = None
|
||||||
created_at = tweet.get("created_at", "")
|
created_at = tweet.get("createdAt") or tweet.get("created_at", "")
|
||||||
if created_at:
|
if created_at:
|
||||||
try:
|
try:
|
||||||
# Try ISO format first
|
# Try ISO format first
|
||||||
@@ -220,16 +222,16 @@ def parse_bird_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Extract user info
|
# Extract user info (Bird uses author.username, older format uses user.screen_name)
|
||||||
user = tweet.get("user", {})
|
author = tweet.get("author", {}) or tweet.get("user", {})
|
||||||
author_handle = user.get("screen_name", "") or tweet.get("author_handle", "")
|
author_handle = author.get("username") or author.get("screen_name", "") or tweet.get("author_handle", "")
|
||||||
|
|
||||||
# Build engagement dict
|
# Build engagement dict (Bird uses camelCase: likeCount, retweetCount, etc.)
|
||||||
engagement = {
|
engagement = {
|
||||||
"likes": tweet.get("like_count") or tweet.get("favorite_count"),
|
"likes": tweet.get("likeCount") or tweet.get("like_count") or tweet.get("favorite_count"),
|
||||||
"reposts": tweet.get("retweet_count"),
|
"reposts": tweet.get("retweetCount") or tweet.get("retweet_count"),
|
||||||
"replies": tweet.get("reply_count"),
|
"replies": tweet.get("replyCount") or tweet.get("reply_count"),
|
||||||
"quotes": tweet.get("quote_count"),
|
"quotes": tweet.get("quoteCount") or tweet.get("quote_count"),
|
||||||
}
|
}
|
||||||
# Convert to int where possible
|
# Convert to int where possible
|
||||||
for key in engagement:
|
for key in engagement:
|
||||||
|
|||||||
Reference in New Issue
Block a user