Skip to content

Commit

Permalink
feature: add ability to upload recent drawings to twitter media (pipp…
Browse files Browse the repository at this point in the history
  • Loading branch information
shihao-guo authored Jan 28, 2025
1 parent 207cf35 commit 88a4e9e
Showing 1 changed file with 108 additions and 8 deletions.
116 changes: 108 additions & 8 deletions my_digital_being/activities/activity_post_recent_memory_tweet.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from typing import Dict, Any, List
from urllib.parse import urlparse

from framework.activity_decorator import activity, ActivityBase, ActivityResult
from framework.api_management import api_manager
Expand Down Expand Up @@ -87,7 +88,11 @@ async def execute(self, shared_data) -> ActivityResult:
new_memories=new_memories,
)

# 6) Use chat skill to generate the tweet text
# 6) Extract drawing URLs and upload to Twitter media
drawing_urls = self._extract_drawing_urls(new_memories)
media_ids = await self._upload_drawings_to_twitter(drawing_urls)

# 7) Use chat skill to generate the tweet text
chat_response = await chat_skill.get_chat_completion(
prompt=prompt_text,
system_prompt=(
Expand All @@ -103,8 +108,8 @@ async def execute(self, shared_data) -> ActivityResult:
if len(tweet_text) > self.max_length:
tweet_text = tweet_text[: self.max_length - 3] + "..."

# 7) Post to Twitter via Composio
post_result = self._post_tweet_via_composio(tweet_text)
# 8) Post to Twitter via Composio
post_result = self._post_tweet_via_composio(tweet_text, media_ids)
if not post_result["success"]:
error_msg = post_result.get(
"error", "Unknown error posting tweet via Composio"
Expand All @@ -119,7 +124,7 @@ async def execute(self, shared_data) -> ActivityResult:
else None
)

# 8) Return success, storing the new memories in "data" so we can skip them next time
# 9) Return success, storing the new memories in "data" so we can skip them next time
logger.info(
f"Successfully posted tweet about recent memories: {tweet_text[:50]}..."
)
Expand Down Expand Up @@ -259,20 +264,23 @@ def _build_chat_prompt(
)
return prompt

def _post_tweet_via_composio(self, tweet_text: str) -> Dict[str, Any]:
def _post_tweet_via_composio(self, tweet_text: str, media_ids: List[str]) -> Dict[str, Any]:
"""
Same as your original PostTweetActivity approach to tweeting via Composio.
Post tweet via Composio with optional media_ids.
"""
try:
from framework.composio_integration import composio_manager

logger.info(
f"Posting tweet via Composio action='{self.composio_action}', text='{tweet_text[:50]}...'"
f"Posting tweet via Composio action='{self.composio_action}', text='{tweet_text[:50]}...', media_count={len(media_ids)}"
)

response = composio_manager._toolset.execute_action(
action=self.composio_action,
params={"text": tweet_text},
params={
"text": tweet_text,
"media__media__ids": media_ids if media_ids else None
},
entity_id="MyDigitalBeing",
)

Expand All @@ -291,3 +299,95 @@ def _post_tweet_via_composio(self, tweet_text: str) -> Dict[str, Any]:
except Exception as e:
logger.error(f"Error in Composio tweet post: {e}", exc_info=True)
return {"success": False, "error": str(e)}

def _extract_drawing_urls(self, memories: List[str]) -> List[str]:
"""
Extract URLs from all DrawActivity entries in memories.
Returns a list of valid URLs, empty list if none found.
"""
drawing_urls = []

for memory in memories:
if memory.startswith("DrawActivity =>"):
try:
# Extract the JSON-like string after '=>'
data_str = memory.split("=>")[1].strip()
# Convert string representation to dict
data = eval(data_str)

# Extract URL from image_data
if 'image_data' in data and 'url' in data['image_data']:
url = data['image_data']['url']
# Validate URL
result = urlparse(url)
if all([result.scheme, result.netloc]):
drawing_urls.append(url)
else:
logger.warning(f"Invalid URL format found in DrawActivity: {url}")
except Exception as e:
logger.error(f"Failed to extract drawing URL: {e}")
continue

return drawing_urls

async def _upload_drawings_to_twitter(self, drawing_urls: List[str]) -> List[str]:
"""
Downloads images from URLs and uploads them to Twitter via Composio.
Returns a list of Twitter media IDs.
"""
import aiohttp
import base64
from framework.composio_integration import composio_manager

media_ids = []

if not drawing_urls:
return media_ids

async with aiohttp.ClientSession() as session:
for url in drawing_urls:
try:
# Download image
async with session.get(url) as response:
if response.status != 200:
logger.warning(f"Failed to download image from {url}: {response.status}")
continue

image_data = await response.read()

# Convert to base64
base64_image = base64.b64encode(image_data).decode('utf-8')

# Extract filename from URL or use default
filename = url.split('/')[-1].split('?')[0] or 'image.png'

# Upload to Twitter via Composio
upload_response = composio_manager._toolset.execute_action(
action="TWITTER_MEDIA_UPLOAD_MEDIA",
params={
"media": {
"name": filename,
"content": base64_image
}
},
entity_id="MyDigitalBeing"
)

# Composio returns 'successfull' instead of 'successful'
if upload_response.get("successful") or upload_response.get("successfull"):
media_id = upload_response.get("media_id") or upload_response.get("data", {}).get("media_id")
if media_id:
media_ids.append(media_id)
logger.info(f"Successfully uploaded image to Twitter, media_id: {media_id}")
else:
logger.warning(f"Upload succeeded but no media_id returned. Response: {upload_response}")
else:
error = upload_response.get("error", "Unknown error")
logger.warning(f"Failed to upload image to Twitter: {error}")

except Exception as e:
logger.error(f"Error uploading image to Twitter: {e}", exc_info=True)
continue

logger.debug(f"Uploaded drawing URLs to Twitter media: {media_ids}")
return media_ids

0 comments on commit 88a4e9e

Please sign in to comment.