From 0cc860d2b7846745c6a480a8c2d4004ee3370d8c Mon Sep 17 00:00:00 2001 From: Cameron Pfiffer Date: Tue, 10 Jun 2025 23:22:50 -0700 Subject: [PATCH] Fix reply tool implementation to match post tool pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove non-existent base_tool import - Implement direct API calls matching post tool structure - Add proper reply reference in post record 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- tools/reply.py | 67 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/tools/reply.py b/tools/reply.py index e96e558..165e596 100644 --- a/tools/reply.py +++ b/tools/reply.py @@ -2,8 +2,7 @@ from typing import Optional from pydantic import BaseModel, Field -from atproto import Client -from tools.base_tool import get_bsky_client, log +from pydantic import BaseModel, Field class ReplyArgs(BaseModel): """Arguments for replying to a Bluesky post.""" @@ -38,8 +37,37 @@ def reply_to_bluesky_post(text: str, reply_to_uri: str, reply_to_cid: str) -> st if not reply_to_cid: raise ValueError("reply_to_cid is required for replies") + import os + import requests + from datetime import datetime, timezone + try: - client = get_bsky_client() + # Get credentials from environment + username = os.getenv("BSKY_USERNAME") + password = os.getenv("BSKY_PASSWORD") + pds_host = os.getenv("PDS_URI", "https://bsky.social") + + if not username or not password: + raise Exception("BSKY_USERNAME and BSKY_PASSWORD environment variables must be set") + + # Create session + session_url = f"{pds_host}/xrpc/com.atproto.server.createSession" + session_data = { + "identifier": username, + "password": password + } + + session_response = requests.post(session_url, json=session_data, timeout=10) + session_response.raise_for_status() + session = session_response.json() + access_token = session.get("accessJwt") + user_did = session.get("did") + + if not access_token or not user_did: + raise Exception("Failed to get access token or DID from session") + + # Build reply record + now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") # Create the reply reference reply_ref = { @@ -47,12 +75,33 @@ def reply_to_bluesky_post(text: str, reply_to_uri: str, reply_to_cid: str) -> st "parent": {"uri": reply_to_uri, "cid": reply_to_cid} } - # Send the reply - response = client.send_post(text=text, reply_to=reply_ref) + post_record = { + "$type": "app.bsky.feed.post", + "text": text, + "createdAt": now, + "reply": reply_ref + } + + # Create the post + create_record_url = f"{pds_host}/xrpc/com.atproto.repo.createRecord" + headers = {"Authorization": f"Bearer {access_token}"} + + create_data = { + "repo": user_did, + "collection": "app.bsky.feed.post", + "record": post_record + } + + post_response = requests.post(create_record_url, headers=headers, json=create_data, timeout=10) + post_response.raise_for_status() + result = post_response.json() + + post_uri = result.get("uri") + handle = session.get("handle", username) + rkey = post_uri.split("/")[-1] if post_uri else "" + post_url = f"https://bsky.app/profile/{handle}/post/{rkey}" - log.info(f"Successfully replied to post {reply_to_uri}") - return f"Successfully posted reply: {response.uri}" + return f"Successfully posted reply to Bluesky!\nReply URL: {post_url}\nText: {text}" except Exception as e: - log.error(f"Failed to reply to post: {str(e)}") - raise \ No newline at end of file + raise Exception(f"Error replying to Bluesky post: {str(e)}") \ No newline at end of file -- 2.51.2