fix: refresh expired bluesky sessions

This commit is contained in:
ziperlee
2026-04-09 23:03:47 +08:00
parent 2020156591
commit 9405aa3fb4
2 changed files with 54 additions and 20 deletions
+38 -20
View File
@@ -75,6 +75,12 @@ def _create_session(handle: str, app_password: str) -> Optional[str]:
return None
def _reset_session_cache() -> None:
global _cached_token, _session_error
_cached_token = None
_session_error = None
def _extract_core_subject(topic: str) -> str:
"""Extract core subject from verbose query for Bluesky search."""
from .query import extract_core_subject
@@ -129,12 +135,6 @@ def search_bluesky(
if not handle or not app_password:
return {"posts": [], "error": "Bluesky credentials not configured"}
# Authenticate
token = _create_session(handle, app_password)
if not token:
error_msg = _session_error or "Bluesky session creation failed (unknown error)"
return {"posts": [], "error": error_msg}
count = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
core_topic = _extract_core_subject(topic)
@@ -148,20 +148,38 @@ def search_bluesky(
}
url = f"{BSKY_SEARCH_URL}?{urlencode(params)}"
try:
response = http.request(
"GET", url,
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
except http.HTTPError as e:
_log(f"Search failed: {e}")
if e.status_code == 403 and e.body and "cloudflare" in e.body.lower():
return {"posts": [], "error": "Bluesky search blocked by Cloudflare (403). This is a network-level block - try a different network or VPN."}
return {"posts": [], "error": f"Bluesky search failed: {e}"}
except Exception as e:
_log(f"Search failed: {e}")
return {"posts": [], "error": f"Bluesky search failed: {type(e).__name__}: {e}"}
def _auth_and_search() -> tuple[Optional[Dict[str, Any]], Optional[str]]:
token = _create_session(handle, app_password)
if not token:
error_msg = _session_error or "Bluesky session creation failed (unknown error)"
return None, error_msg
try:
response = http.request(
"GET", url,
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
return response, None
except http.HTTPError as e:
_log(f"Search failed: {e}")
if e.status_code == 401:
_reset_session_cache()
return None, "refresh"
if e.status_code == 403 and e.body and "cloudflare" in e.body.lower():
return None, "Bluesky search blocked by Cloudflare (403). This is a network-level block - try a different network or VPN."
return None, f"Bluesky search failed: {e}"
except Exception as e:
_log(f"Search failed: {e}")
return None, f"Bluesky search failed: {type(e).__name__}: {e}"
response, error_msg = _auth_and_search()
if error_msg == "refresh":
_log("Session expired; recreating token and retrying once")
response, error_msg = _auth_and_search()
if error_msg:
return {"posts": [], "error": error_msg}
if response is None:
return {"posts": [], "error": "Bluesky search failed (unknown error)"}
posts = response.get("posts", [])
_log(f"Found {len(posts)} posts")
+16
View File
@@ -194,6 +194,22 @@ class TestSearchBlueskyAuth(unittest.TestCase):
search_call = mock_request.call_args_list[1]
self.assertEqual(search_call.kwargs.get("headers", {}), {"Authorization": "Bearer tok123"})
@patch("lib.bluesky.http.request")
def test_401_search_refreshes_session_once(self, mock_request):
from lib.http import HTTPError
mock_request.side_effect = [
{"accessJwt": "tok-old", "refreshJwt": "ref-old"},
HTTPError("HTTP 401: Unauthorized", 401, ""),
{"accessJwt": "tok-new", "refreshJwt": "ref-new"},
{"posts": [{"uri": "at://did/app.bsky.feed.post/abc", "author": {"handle": "u1"}, "record": {"text": "hi"}}]},
]
config = {"BSKY_HANDLE": "user.bsky.social", "BSKY_APP_PASSWORD": "pw"}
result = bluesky.search_bluesky("test", "2026-01-01", "2026-03-09", config=config)
self.assertEqual(len(result["posts"]), 1)
self.assertEqual(mock_request.call_count, 4)
self.assertEqual(mock_request.call_args_list[3].kwargs.get("headers", {}), {"Authorization": "Bearer tok-new"})
if __name__ == "__main__":
unittest.main()