-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature branch farcaster support #71
base: master
Are you sure you want to change the base?
Feature branch farcaster support #71
Conversation
🟡 Heimdall Review Status
|
Hey @John-peterson-coinbase I've added the plugin, the example with the chatbot and the different tests. IMO i believe there is a lot of useless tests but ive added them for the coverage, let me know if there is no need of a given coverage to know if i should delete them of leave them. Also let me know if you need any pictures of it working. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM! Main comment is on pushing implementation logic for actions into cdp-agentkit-core
so it is more easily reusable.
def get_user_details(self, fid: Optional[str] = None) -> str: | ||
"""Get details about a Farcaster user. | ||
|
||
Args: | ||
fid (str, optional): The FID (Farcaster ID) of the user. | ||
If not provided, uses the authenticated user's FID. | ||
|
||
Returns: | ||
str: User details in a formatted string. | ||
""" | ||
try: | ||
# Use provided FID or default to authenticated user | ||
target_fid = fid or self.fid | ||
response = self._make_request("GET", f"user/bulk?fids={target_fid}") | ||
|
||
# Check if we got user data | ||
if "users" in response and response["users"]: | ||
user = response["users"][0] # Get the first user since we only requested one | ||
return (f"Retrieved user details for FID {target_fid}:\n" | ||
f"Username: {user.get('username', 'N/A')}\n" | ||
f"Display Name: {user.get('display_name', 'N/A')}\n" | ||
f"Profile Image: {user.get('pfp_url', 'N/A')}\n" | ||
f"Followers: {user.get('followers_count', 0)}\n" | ||
f"Following: {user.get('following_count', 0)}") | ||
else: | ||
return f"No user details found for FID {target_fid}" | ||
|
||
except Exception as e: | ||
print(f"Error response: {str(e)}") # Debug print | ||
return f"Failed to get user details: {str(e)}" | ||
|
||
def get_user_casts(self, fid: str, limit: int = 10) -> str: | ||
"""Get recent casts from a user. | ||
|
||
Args: | ||
fid (str): The FID (Farcaster ID) of the user. | ||
limit (int): Number of casts to retrieve (max 100). | ||
|
||
Returns: | ||
str: A message containing the retrieved casts. | ||
""" | ||
if limit > 100: | ||
raise ValueError("Cannot retrieve more than 100 casts at once") | ||
|
||
try: | ||
response = self._make_request("GET", f"user/cast?fid={fid}&limit={limit}") | ||
return f"Retrieved {len(response.get('casts', []))} casts from user {fid}" | ||
except Exception as e: | ||
return f"Failed to get user casts: {str(e)}" | ||
|
||
def run_action(self, func: Callable[..., str], **kwargs) -> str: | ||
"""Run a Farcaster Action. | ||
|
||
Args: | ||
func (Callable): The function to run | ||
**kwargs: Arguments to pass to the function | ||
|
||
Returns: | ||
str: The result of the action | ||
""" | ||
try: | ||
return func(self, **kwargs) | ||
except Exception as e: | ||
return f"Error executing action: {str(e)}" | ||
|
||
def get_notifications(self, fid: Optional[str] = None, notification_types: Optional[List[str]] = None) -> str: | ||
"""Get notifications for a user.""" | ||
try: | ||
response = self._make_request("GET", f"notifications?fid={fid or self.fid}") | ||
if response and "notifications" in response: | ||
result = [] | ||
|
||
for notif in response["notifications"]: | ||
if notif["type"] == "likes": | ||
# Handle likes | ||
for reaction in notif["reactions"]: | ||
username = reaction["user"].get("username", "unknown") | ||
result.append(f"❤️ @{username} liked your cast") | ||
|
||
elif notif["type"] == "follows": | ||
# Handle follows | ||
for follow in notif["follows"]: | ||
username = follow["user"].get("username", "unknown") | ||
result.append(f"👥 @{username} followed you") | ||
|
||
elif notif["type"] == "reply": | ||
# Handle replies | ||
username = notif["cast"]["author"].get("username", "unknown") | ||
text = notif["cast"].get("text", "") | ||
result.append(f"💬 @{username} replied: \"{text}\"") | ||
|
||
elif notif["type"] == "recasts": | ||
# Handle recasts | ||
for reaction in notif["reactions"]: | ||
username = reaction["user"].get("username", "unknown") | ||
result.append(f"🔄 @{username} recasted your cast") | ||
|
||
return "\nRecent Notifications:\n" + "\n".join(result) | ||
return "No notifications found" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of implementing the business logic of each action func
in the Langchain extension, can we move this into cdp-agentkit-core
for each action?
An example from one of the twitter actions
With this pattern, we avoid repeating ourselves when we extend to other AI frameworks.
### Farcaster Account Setup | ||
1. Create a Farcaster account if you haven't already | ||
2. Get your Neynar API key from [Neynar Dashboard](https://neynar.com/) | ||
3. Create a signer using the Neynar API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this warrant an example or hyperlink?
func = lambda client, text, channel_id=None, embeds=None: client.cast(text=text, channel_id=channel_id, embeds=embeds) | ||
|
||
|
||
# FARCASTER_ACTIONS = [CastAction()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# FARCASTER_ACTIONS = [CastAction()] |
What changed? Why?
Added farcaster plugin using neynar API.
Qualified Impact
New support for farcaster