diff --git a/tools/post.py b/tools/post.py index b3a7c2a..c69b2a2 100644 --- a/tools/post.py +++ b/tools/post.py @@ -6,8 +6,23 @@ class PostArgs(BaseModel): text: str = Field(..., description="The text content to post (max 300 characters)") -def post_to_bluesky(text: str) -> str: - """Post a message to Bluesky.""" +def create_new_bluesky_post(text: str) -> str: + """ + Create a NEW standalone post on Bluesky. This tool creates independent posts that + start new conversations. + + IMPORTANT: This tool is ONLY for creating new posts. To reply to an existing post, + use reply_to_bluesky_post instead. + + Args: + text: The post content (max 300 characters) + + Returns: + Success message with post URL + + Raises: + Exception: If the post fails + """ import os import requests from datetime import datetime, timezone diff --git a/tools/reply.py b/tools/reply.py new file mode 100644 index 0000000..e96e558 --- /dev/null +++ b/tools/reply.py @@ -0,0 +1,58 @@ +"""Reply to Bluesky posts tool.""" + +from typing import Optional +from pydantic import BaseModel, Field +from atproto import Client +from tools.base_tool import get_bsky_client, log + +class ReplyArgs(BaseModel): + """Arguments for replying to a Bluesky post.""" + text: str = Field(..., description="The reply text content (max 300 characters)") + reply_to_uri: str = Field(..., description="The AT URI of the post being replied to (required)") + reply_to_cid: str = Field(..., description="The CID of the post being replied to (required)") + +def reply_to_bluesky_post(text: str, reply_to_uri: str, reply_to_cid: str) -> str: + """ + Reply to an existing Bluesky post. This tool creates a threaded reply to a specific post. + + IMPORTANT: This tool is ONLY for replying to existing posts. To create a new standalone post, + use create_new_bluesky_post instead. + + Args: + text: The reply text content (max 300 characters) + reply_to_uri: The AT URI of the post being replied to (required) + reply_to_cid: The CID of the post being replied to (required) + + Returns: + Success message with post URI or error message + + Raises: + Exception: If the reply fails + """ + if len(text) > 300: + raise ValueError(f"Reply text too long: {len(text)} characters (max 300)") + + if not reply_to_uri: + raise ValueError("reply_to_uri is required for replies") + + if not reply_to_cid: + raise ValueError("reply_to_cid is required for replies") + + try: + client = get_bsky_client() + + # Create the reply reference + reply_ref = { + "root": {"uri": reply_to_uri, "cid": reply_to_cid}, + "parent": {"uri": reply_to_uri, "cid": reply_to_cid} + } + + # Send the reply + response = client.send_post(text=text, reply_to=reply_ref) + + log.info(f"Successfully replied to post {reply_to_uri}") + return f"Successfully posted reply: {response.uri}" + + except Exception as e: + log.error(f"Failed to reply to post: {str(e)}") + raise \ No newline at end of file