Initial commit: twitter-cli v0.1.0
This commit is contained in:
125
twitter_cli/auth.py
Normal file
125
twitter_cli/auth.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Cookie authentication for Twitter/X.
|
||||
|
||||
Supports:
|
||||
1. Environment variables: TWITTER_AUTH_TOKEN + TWITTER_CT0
|
||||
2. Auto-extract from browser via browser-cookie3 (subprocess)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Dict, Optional
|
||||
|
||||
|
||||
def load_from_env() -> Optional[Dict[str, str]]:
|
||||
"""Load cookies from environment variables."""
|
||||
auth_token = os.environ.get("TWITTER_AUTH_TOKEN", "")
|
||||
ct0 = os.environ.get("TWITTER_CT0", "")
|
||||
if auth_token and ct0:
|
||||
return {"auth_token": auth_token, "ct0": ct0}
|
||||
return None
|
||||
|
||||
|
||||
def extract_from_browser(browser: str = "chrome") -> Optional[Dict[str, str]]:
|
||||
"""Auto-extract cookies from local browser using browser-cookie3.
|
||||
|
||||
Runs in a subprocess to avoid SQLite database lock issues when the
|
||||
browser is running.
|
||||
"""
|
||||
extract_script = '''
|
||||
import json, sys
|
||||
try:
|
||||
import browser_cookie3
|
||||
except ImportError:
|
||||
print(json.dumps({"error": "browser-cookie3 not installed"}))
|
||||
sys.exit(1)
|
||||
|
||||
browser_funcs = {
|
||||
"chrome": browser_cookie3.chrome,
|
||||
"firefox": browser_cookie3.firefox,
|
||||
"edge": browser_cookie3.edge,
|
||||
"brave": browser_cookie3.brave,
|
||||
}
|
||||
|
||||
browser_name = "%s"
|
||||
fn = browser_funcs.get(browser_name)
|
||||
if not fn:
|
||||
print(json.dumps({"error": "Unsupported browser: " + browser_name}))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
jar = fn()
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
result = {}
|
||||
for cookie in jar:
|
||||
domain = cookie.domain or ""
|
||||
if domain.endswith(".x.com") or domain.endswith(".twitter.com") or domain in ("x.com", "twitter.com", ".x.com", ".twitter.com"):
|
||||
if cookie.name == "auth_token":
|
||||
result["auth_token"] = cookie.value
|
||||
elif cookie.name == "ct0":
|
||||
result["ct0"] = cookie.value
|
||||
|
||||
if "auth_token" in result and "ct0" in result:
|
||||
print(json.dumps(result))
|
||||
else:
|
||||
print(json.dumps({"error": "Could not find auth_token and ct0 cookies. Make sure you are logged into x.com in " + browser_name + "."}))
|
||||
sys.exit(1)
|
||||
''' % browser
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-c", extract_script],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15,
|
||||
)
|
||||
output = result.stdout.strip()
|
||||
if not output:
|
||||
stderr = result.stderr.strip()
|
||||
if stderr:
|
||||
# Maybe browser-cookie3 not installed, try with uv
|
||||
result2 = subprocess.run(
|
||||
["uv", "run", "--with", "browser-cookie3", "python3", "-c", extract_script],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
)
|
||||
output = result2.stdout.strip()
|
||||
if not output:
|
||||
return None
|
||||
|
||||
data = json.loads(output)
|
||||
if "error" in data:
|
||||
return None
|
||||
return {"auth_token": data["auth_token"], "ct0": data["ct0"]}
|
||||
except (subprocess.TimeoutExpired, json.JSONDecodeError, KeyError, FileNotFoundError):
|
||||
return None
|
||||
|
||||
|
||||
def get_cookies(browser: str = "chrome") -> Dict[str, str]:
|
||||
"""Get Twitter cookies. Priority: env vars -> browser extraction.
|
||||
|
||||
Returns dict with 'auth_token' and 'ct0' keys.
|
||||
Raises RuntimeError if no cookies found.
|
||||
"""
|
||||
# 1. Try environment variables
|
||||
env_cookies = load_from_env()
|
||||
if env_cookies:
|
||||
return env_cookies
|
||||
|
||||
# 2. Try browser extraction
|
||||
browser_cookies = extract_from_browser(browser)
|
||||
if browser_cookies:
|
||||
return browser_cookies
|
||||
|
||||
raise RuntimeError(
|
||||
"No Twitter cookies found.\n"
|
||||
"Option 1: Set TWITTER_AUTH_TOKEN and TWITTER_CT0 environment variables\n"
|
||||
"Option 2: Make sure you are logged into x.com in your browser"
|
||||
)
|
||||
Reference in New Issue
Block a user