Document env-based X auth flow
Update README, launch copy, and UI guidance to prefer popup-free AUTH_TOKEN/CT0 configuration, and keep X backend selection on the verified Bird or xAI paths. Validation: uv run python -m unittest tests.test_env_project
This commit is contained in:
@@ -1621,8 +1621,8 @@ def main():
|
||||
# Check available sources (accounting for Bird auto-detection)
|
||||
available = env.get_available_sources(config)
|
||||
|
||||
# Override available if Bird or ScrapeCreators provides X
|
||||
if x_source in ('bird', 'scrapecreators'):
|
||||
# Override available if Bird provides X
|
||||
if x_source == 'bird':
|
||||
if available == 'reddit':
|
||||
available = 'both' # Now have both Reddit + X
|
||||
elif available == 'reddit-web':
|
||||
|
||||
+5
-14
@@ -346,7 +346,7 @@ def get_web_search_source(config: Dict[str, Any]) -> Optional[str]:
|
||||
|
||||
|
||||
def get_missing_keys(config: Dict[str, Any]) -> str:
|
||||
"""Determine which sources are missing (accounting for Bird and ScrapeCreators).
|
||||
"""Determine which sources are missing (accounting for Bird).
|
||||
|
||||
Returns: 'all', 'both', 'reddit', 'x', 'web', or 'none'
|
||||
"""
|
||||
@@ -358,8 +358,7 @@ def get_missing_keys(config: Dict[str, Any]) -> str:
|
||||
from . import bird_x
|
||||
has_bird = bird_x.is_bird_installed() and bird_x.is_bird_authenticated()
|
||||
|
||||
has_sc_x = bool(config.get('SCRAPECREATORS_API_KEY'))
|
||||
has_x = has_xai or has_bird or has_sc_x
|
||||
has_x = has_xai or has_bird
|
||||
|
||||
if has_reddit and has_x and has_web:
|
||||
return 'none'
|
||||
@@ -438,7 +437,9 @@ def validate_sources(requested: str, available: str, include_web: bool = False)
|
||||
def get_x_source(config: Dict[str, Any]) -> Optional[str]:
|
||||
"""Determine the best available X/Twitter source.
|
||||
|
||||
Priority: Bird (free) → xAI (paid API) → ScrapeCreators (shared key)
|
||||
Priority: Bird (free) → xAI (paid API)
|
||||
|
||||
Keep X selection limited to documented, verified search backends.
|
||||
|
||||
Args:
|
||||
config: Configuration dict from get_config()
|
||||
@@ -446,7 +447,6 @@ def get_x_source(config: Dict[str, Any]) -> Optional[str]:
|
||||
Returns:
|
||||
'bird' if Bird is installed and authenticated,
|
||||
'xai' if XAI_API_KEY is configured,
|
||||
'scrapecreators' if SCRAPECREATORS_API_KEY is configured,
|
||||
None if no X source available.
|
||||
"""
|
||||
# Import here to avoid circular dependency
|
||||
@@ -462,10 +462,6 @@ def get_x_source(config: Dict[str, Any]) -> Optional[str]:
|
||||
if config.get('XAI_API_KEY'):
|
||||
return 'xai'
|
||||
|
||||
# Fall back to ScrapeCreators (same key as Reddit/TikTok/Instagram)
|
||||
if config.get('SCRAPECREATORS_API_KEY'):
|
||||
return 'scrapecreators'
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -584,15 +580,11 @@ def get_x_source_status(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
bird_status = bird_x.get_bird_status()
|
||||
xai_available = bool(config.get('XAI_API_KEY'))
|
||||
|
||||
sc_available = bool(config.get('SCRAPECREATORS_API_KEY'))
|
||||
|
||||
# Determine active source
|
||||
if bird_status["authenticated"]:
|
||||
source = 'bird'
|
||||
elif xai_available:
|
||||
source = 'xai'
|
||||
elif sc_available:
|
||||
source = 'scrapecreators'
|
||||
else:
|
||||
source = None
|
||||
|
||||
@@ -602,6 +594,5 @@ def get_x_source_status(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"bird_authenticated": bird_status["authenticated"],
|
||||
"bird_username": bird_status["username"],
|
||||
"xai_available": xai_available,
|
||||
"scrapecreators_available": sc_available,
|
||||
"can_install_bird": bird_status["can_install"],
|
||||
}
|
||||
|
||||
+9
-10
@@ -143,7 +143,7 @@ Just start with "last30" and talk to me like normal.
|
||||
# Shorter promo for single missing key
|
||||
PROMO_SINGLE_KEY = {
|
||||
"reddit": "\n💡 You can unlock Reddit with an OpenAI API key or by running `codex login` — just ask me how.\n",
|
||||
"x": "\n💡 You can unlock X with an xAI API key — just ask me how.\n",
|
||||
"x": "\n💡 You can unlock X with AUTH_TOKEN/CT0 or XAI_API_KEY - just ask me how.\n",
|
||||
}
|
||||
|
||||
# Bird auth help (for local users with vendored Bird CLI)
|
||||
@@ -151,16 +151,16 @@ BIRD_AUTH_HELP = f"""
|
||||
{Colors.YELLOW}Bird authentication failed.{Colors.RESET}
|
||||
|
||||
To fix this:
|
||||
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
|
||||
2. Try again — Bird reads your browser cookies automatically.
|
||||
1. Add AUTH_TOKEN and CT0 to ~/.config/last30days/.env or .claude/last30days.env
|
||||
2. Or set XAI_API_KEY for the xAI fallback backend
|
||||
"""
|
||||
|
||||
BIRD_AUTH_HELP_PLAIN = """
|
||||
Bird authentication failed.
|
||||
|
||||
To fix this:
|
||||
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
|
||||
2. Try again — Bird reads your browser cookies automatically.
|
||||
1. Add AUTH_TOKEN and CT0 to ~/.config/last30days/.env or .claude/last30days.env
|
||||
2. Or set XAI_API_KEY for the xAI fallback backend
|
||||
"""
|
||||
|
||||
# Spinner frames
|
||||
@@ -460,10 +460,9 @@ def show_diagnostic_banner(diag: dict):
|
||||
label = f"Bird ({username})" if source == "bird" and username else source.upper()
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ X/Twitter{Colors.RESET} — {label} {Colors.DIM}│{Colors.RESET}")
|
||||
else:
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.RED}❌ X/Twitter{Colors.RESET} — No Bird CLI or XAI_API_KEY {Colors.DIM}│{Colors.RESET}")
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.RED}❌ X/Twitter{Colors.RESET} — No X auth or fallback key {Colors.DIM}│{Colors.RESET}")
|
||||
if diag.get("bird_installed"):
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Bird installed but not authenticated {Colors.DIM}│{Colors.RESET}")
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Log into x.com in your browser, then retry {Colors.DIM}│{Colors.RESET}")
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Add AUTH_TOKEN/CT0 or XAI_API_KEY {Colors.DIM}│{Colors.RESET}")
|
||||
else:
|
||||
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Needs Node.js 22+ (Bird is bundled) {Colors.DIM}│{Colors.RESET}")
|
||||
|
||||
@@ -507,9 +506,9 @@ def show_diagnostic_banner(diag: dict):
|
||||
if has_x:
|
||||
lines.append("│ ✅ X/Twitter — available │")
|
||||
else:
|
||||
lines.append("│ ❌ X/Twitter — No Bird CLI or XAI_API_KEY │")
|
||||
lines.append("│ ❌ X/Twitter — No X auth or fallback key │")
|
||||
if diag.get("bird_installed"):
|
||||
lines.append("│ └─ Log into x.com in your browser, then retry │")
|
||||
lines.append("│ └─ Add AUTH_TOKEN/CT0 or XAI_API_KEY │")
|
||||
else:
|
||||
lines.append("│ └─ Needs Node.js 22+ (Bird is bundled) │")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user