diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f302871..6847b87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,6 +101,8 @@ jobs: kubectl set image -n testnet-dev deployment/intent-autonomous intent-autonomous=crestal/intentkit:${VERSION_NO_V} kubectl set image -n testnet-dev deployment/intent-twitter intent-twitter=crestal/intentkit:${VERSION_NO_V} kubectl set image -n testnet-dev deployment/intent-tg intent-tg=crestal/intentkit:${VERSION_NO_V} + kubectl set image -n testnet-dev deployment/intent-scheduler intent-scheduler=crestal/intentkit:${VERSION_NO_V} + kubectl set image -n testnet-dev deployment/intent-singleton intent-singleton=crestal/intentkit:${VERSION_NO_V} - name: Deploy to Amazon EKS Prod if: ${{ github.event_name == 'release' && github.event.action == 'released' }} @@ -114,6 +116,8 @@ jobs: kubectl set image -n testnet-prod deployment/intent-autonomous intent-autonomous=crestal/intentkit:${VERSION_NO_V} kubectl set image -n testnet-prod deployment/intent-twitter intent-twitter=crestal/intentkit:${VERSION_NO_V} kubectl set image -n testnet-prod deployment/intent-tg intent-tg=crestal/intentkit:${VERSION_NO_V} + kubectl set image -n testnet-prod deployment/intent-scheduler intent-scheduler=crestal/intentkit:${VERSION_NO_V} + kubectl set image -n testnet-prod deployment/intent-singleton intent-singleton=crestal/intentkit:${VERSION_NO_V} - name: Build Success if: ${{ success() }} diff --git a/abstracts/twitter.py b/abstracts/twitter.py index e2af292..f402d9d 100644 --- a/abstracts/twitter.py +++ b/abstracts/twitter.py @@ -1,11 +1,8 @@ from abc import ABC, abstractmethod -from datetime import datetime from typing import Optional from tweepy.asynchronous import AsyncClient -from models.agent import AgentData - class TwitterABC(ABC): """Abstract base class for Twitter operations. @@ -14,29 +11,11 @@ class TwitterABC(ABC): through a Tweepy client. """ + agent_id: str use_key = False - need_auth = True - - @abstractmethod - async def initialize(self) -> None: - """Initialize the Twitter client with OAuth2 tokens if available.""" - pass @abstractmethod - async def update_tokens( - self, access_token: str, refresh_token: str, expires_at: datetime - ) -> None: - """Update OAuth2 tokens in agent data. - - Args: - access_token: New access token - refresh_token: New refresh token - expires_at: Token expiration timestamp - """ - pass - - @abstractmethod - def get_client(self) -> Optional[AsyncClient]: + async def get_client(self) -> Optional[AsyncClient]: """Get a configured Tweepy client. Returns: @@ -44,15 +23,6 @@ def get_client(self) -> Optional[AsyncClient]: """ pass - @abstractmethod - def get_agent_data(self) -> Optional[AgentData]: - """Get the agent data. - - Returns: - Optional[AgentData]: The agent data if available, None otherwise - """ - pass - @property @abstractmethod def self_id(self) -> Optional[str]: diff --git a/app/core/engine.py b/app/core/engine.py index 78f2be0..3864e72 100644 --- a/app/core/engine.py +++ b/app/core/engine.py @@ -53,7 +53,6 @@ # Global dictionaries to cache agent update times agents_updated: dict[str, datetime] = {} -agents_data_updated: dict[str, datetime] = {} def agent_prompt(agent: Agent) -> str: @@ -123,7 +122,6 @@ async def initialize_agent(aid): # Cache the update times agents_updated[aid] = agent.updated_at - agents_data_updated[aid] = agent_data.updated_at if agent_data else None except NoResultFound: # Handle the case where the user is not found raise HTTPException(status_code=404, detail="Agent not found") @@ -227,33 +225,21 @@ async def initialize_agent(aid): if agent.twitter_skills and len(agent.twitter_skills) > 0: if not agent.twitter_config: agent.twitter_config = {} - try: - twitter_client = TwitterClient(agent_store, agent.twitter_config) - await twitter_client.initialize() - if not twitter_client.need_auth: - for skill in agent.twitter_skills: - try: - s = get_twitter_skill( - skill, - twitter_client, - skill_store, - aid, - agent_store, - ) - tools.append(s) - except Exception as e: - logger.warning( - f"Failed to initialize Twitter skill {skill}: {e}" - ) - twitter_prompt = ( - f"\n\nYour twitter id is {agent_data.twitter_id}, never reply or retweet yourself. " - f"Your twitter username is {agent_data.twitter_username}. \n" - f"Your twitter name is {agent_data.twitter_name}. \n" - ) - else: - logger.info(f"Twitter client needs authentication for agent {aid}") - except Exception as e: - logger.warning(f"Failed to initialize Twitter client for agent {aid}: {e}") + twitter_client = TwitterClient(aid, agent_store, agent.twitter_config) + for skill in agent.twitter_skills: + s = get_twitter_skill( + skill, + twitter_client, + skill_store, + aid, + agent_store, + ) + tools.append(s) + twitter_prompt = ( + f"\n\nYour twitter id is {agent_data.twitter_id}, never reply or retweet yourself. " + f"Your twitter username is {agent_data.twitter_username}. \n" + f"Your twitter name is {agent_data.twitter_name}. \n" + ) # Crestal skills if agent.crestal_skills: @@ -358,17 +344,11 @@ async def execute_agent( agent = await Agent.get(aid) if not agent: raise HTTPException(status_code=404, detail="Agent not found") - agent_data = await AgentData.get(aid) # Check if agent needs reinitialization due to updates needs_reinit = False if aid in agents: - if ( - aid not in agents_updated - or agent.updated_at != agents_updated[aid] - or aid not in agents_data_updated - or (agent_data and agent_data.updated_at != agents_data_updated[aid]) - ): + if aid not in agents_updated or agent.updated_at != agents_updated[aid]: needs_reinit = True logger.info(f"Reinitializing agent {aid} due to updates") @@ -378,13 +358,13 @@ async def execute_agent( ) # cold start or needs reinitialization - if aid not in agents or needs_reinit: - await initialize_agent(aid) + if (aid not in agents) or needs_reinit: resp_debug.append( "[ Agent cold start ... ]" if aid not in agents else "[ Agent reinitialized ... ]" ) + await initialize_agent(aid) resp_debug.append( f"\n------------------- start cost: {time.perf_counter() - last:.3f} seconds\n" ) @@ -402,33 +382,20 @@ async def execute_agent( ] ) # debug prompt - # if debug: - # # get the agent from the database - # with get_session() as db: - # try: - # agent: Agent = db.exec(select(Agent).filter(Agent.id == aid)).one() - # except NoResultFound: - # # Handle the case where the user is not found - # raise HTTPException(status_code=404, detail="Agent not found") - # except SQLAlchemyError as e: - # # Handle other SQLAlchemy-related errors - # logger.error(e) - # raise HTTPException(status_code=500, detail=str(e)) - # try: - # resp_debug_append = "\n===================\n\n[ system ]\n" - # resp_debug_append += agent_prompt(agent) - # snap = executor.get_state(stream_config) - # if snap.values and "messages" in snap.values: - # for msg in snap.values["messages"]: - # resp_debug_append += f"[ {msg.type} ]\n{str(msg.content)}\n\n" - # if agent.prompt_append: - # resp_debug_append += "[ system ]\n" - # resp_debug_append += agent.prompt_append - # except Exception as e: - # logger.error( - # "failed to get debug prompt: " + str(e), exc_info=True, stack_info=True - # ) - # resp_debug_append = "" + if debug: + try: + resp_debug_append = "\n===================\n\n[ system ]\n" + resp_debug_append += agent_prompt(agent) + snap = await executor.aget_state(stream_config) + if snap.values and "messages" in snap.values: + for msg in snap.values["messages"]: + resp_debug_append += f"[ {msg.type} ]\n{str(msg.content)}\n\n" + if agent.prompt_append: + resp_debug_append += "[ system ]\n" + resp_debug_append += agent.prompt_append + except Exception as e: + logger.error(f"failed to get debug prompt: {e}") + resp_debug_append = "" # run async for chunk in executor.astream( {"messages": [HumanMessage(content=content)]}, stream_config @@ -456,7 +423,7 @@ async def execute_agent( total_time = time.perf_counter() - start resp_debug.append(f"Total time cost: {total_time:.3f} seconds") if debug: - # resp_debug.append(resp_debug_append) + resp_debug.append(resp_debug_append) return resp_debug else: return resp diff --git a/app/services/twitter/client.py b/app/services/twitter/client.py index 685f2c8..519b584 100644 --- a/app/services/twitter/client.py +++ b/app/services/twitter/client.py @@ -22,127 +22,103 @@ class TwitterClient(TwitterABC): config: Configuration dictionary that may contain API keys """ - def __init__(self, agent_store: AgentStoreABC, config: Dict) -> None: + def __init__(self, agent_id, agent_store: AgentStoreABC, config: Dict) -> None: """Initialize the Twitter client. Args: agent_store: The agent store for persisting data config: Configuration dictionary that may contain API keys """ + self.agent_id = agent_id self._client: Optional[AsyncClient] = None self._agent_store = agent_store self._agent_data: Optional[AgentData] = None self.use_key = False - self.need_auth = False - - # Check if we have API keys in config - if all( - key in config and config[key] - for key in [ - "consumer_key", - "consumer_secret", - "access_token", - "access_token_secret", - ] - ): - self._client = AsyncClient( - consumer_key=config["consumer_key"], - consumer_secret=config["consumer_secret"], - access_token=config["access_token"], - access_token_secret=config["access_token_secret"], - return_type=dict, - ) - self.use_key = True - return - - # Otherwise try to get OAuth2 tokens from agent data - self._agent_data = None - self.need_auth = True - - async def initialize(self) -> None: - """Initialize the Twitter client with OAuth2 tokens if available.""" - if self.use_key: - me = await self._client.get_me(user_auth=self.use_key) - if me and "data" in me and "id" in me["data"]: - await self._agent_store.set_data( - { - "twitter_id": me["data"]["id"], - "twitter_username": me["data"]["username"], - "twitter_name": me["data"]["name"], - } - ) - self._agent_data = await self._agent_store.get_data() - logger.info( - f"Twitter client initialized. " - f"Use API key: {self.use_key}, " - f"User ID: {self.self_id}, " - f"Username: {self.self_username}, " - f"Name: {self.self_name}" - ) - return + self._config = config - self._agent_data = await self._agent_store.get_data() - if not self._agent_data: - return + async def get_client(self) -> Optional[AsyncClient]: + """Get the initialized Twitter client. - if ( - self._agent_data.twitter_access_token - and self._agent_data.twitter_access_token_expires_at - ): - # Check if token is expired + Returns: + Optional[AsyncClient]: The Twitter client if initialized, None otherwise + """ + if not self._agent_data: + self._agent_data = await self._agent_store.get_data() + if not self._agent_data: + raise Exception(f"[{self.agent_id}] Agent data not found") + if not self._client: + # Check if we have API keys in config + if self._config and all( + key in self._config and self._config[key] + for key in [ + "consumer_key", + "consumer_secret", + "access_token", + "access_token_secret", + ] + ): + self._client = AsyncClient( + consumer_key=self._config["consumer_key"], + consumer_secret=self._config["consumer_secret"], + access_token=self._config["access_token"], + access_token_secret=self._config["access_token_secret"], + return_type=dict, + ) + self.use_key = True + me = await self._client.get_me(user_auth=self.use_key) + if me and "data" in me and "id" in me["data"]: + await self._agent_store.set_data( + { + "twitter_id": me["data"]["id"], + "twitter_username": me["data"]["username"], + "twitter_name": me["data"]["name"], + } + ) + self._agent_data = await self._agent_store.get_data() + logger.info( + f"Twitter client initialized. " + f"Use API key: {self.use_key}, " + f"User ID: {self.self_id}, " + f"Username: {self.self_username}, " + f"Name: {self.self_name}" + ) + return self._client + # Otherwise try to get OAuth2 tokens from agent data + if not self._agent_data.twitter_access_token: + raise Exception(f"[{self.agent_id}] Twitter access token not found") + if not self._agent_data.twitter_access_token_expires_at: + raise Exception( + f"[{self.agent_id}] Twitter access token expiration not found" + ) if self._agent_data.twitter_access_token_expires_at <= datetime.now( tz=timezone.utc ): - self.need_auth = True - return - - # Initialize client with access token + raise Exception(f"[{self.agent_id}] Twitter access token has expired") self._client = AsyncClient( bearer_token=self._agent_data.twitter_access_token, return_type=dict, ) - self.need_auth = False - - async def update_tokens( - self, access_token: str, refresh_token: str, expires_at: datetime - ) -> None: - """Update OAuth2 tokens in agent data. - - Args: - access_token: New access token - refresh_token: New refresh token - expires_at: Token expiration timestamp - """ - if not self._agent_data: - self._agent_data = await self._agent_store.get_data() - if not self._agent_data: - return - - self._agent_data.twitter_access_token = access_token - self._agent_data.twitter_refresh_token = refresh_token - self._agent_data.twitter_access_token_expires_at = expires_at - await self._agent_store.set_data(self._agent_data) - - # Update client with new access token - self._client = AsyncClient(bearer_token=access_token, return_type=dict) - self.need_auth = False - - def get_client(self) -> Optional[AsyncClient]: - """Get the initialized Twitter client. - - Returns: - Optional[AsyncClient]: The Twitter client if initialized, None otherwise - """ + return self._client + if not self.use_key: + # check if access token has expired + if self._agent_data.twitter_access_token_expires_at <= datetime.now( + tz=timezone.utc + ): + self._agent_data = await self._agent_store.get_data() + # check again + if self._agent_data.twitter_access_token_expires_at <= datetime.now( + tz=timezone.utc + ): + raise Exception( + f"[{self.agent_id}] Twitter access token has expired" + ) + self._client = AsyncClient( + bearer_token=self._agent_data.twitter_access_token, + return_type=dict, + ) + return self._client return self._client - def get_agent_data(self) -> Optional[AgentData]: - """Get the agent data. - - Returns: - Optional[AgentData]: The agent data if available, None otherwise - """ - return self._agent_data - @property def self_id(self) -> Optional[str]: """Get the Twitter user ID. diff --git a/app/services/twitter/oauth2_callback.py b/app/services/twitter/oauth2_callback.py index 9e665ae..8baab6c 100644 --- a/app/services/twitter/oauth2_callback.py +++ b/app/services/twitter/oauth2_callback.py @@ -3,14 +3,12 @@ from datetime import datetime, timezone import tweepy -from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException -from sqlmodel.ext.asyncio.session import AsyncSession +from fastapi import APIRouter, HTTPException from starlette.responses import JSONResponse from app.config.config import config from app.services.twitter.oauth2 import oauth2_user_handler from models.agent import Agent, AgentData -from models.db import get_db router = APIRouter(prefix="/callback/auth", tags=["Callback"]) @@ -19,8 +17,6 @@ async def twitter_oauth_callback( state: str, code: str, - background_tasks: BackgroundTasks, - db: AsyncSession = Depends(get_db), ): """Handle Twitter OAuth2 callback. @@ -31,8 +27,6 @@ async def twitter_oauth_callback( Args: state: Agent ID from authorization request code: Authorization code from Twitter - background_tasks: FastAPI background tasks - db: Database session from FastAPI dependency injection Returns: JSONResponse with success message @@ -45,15 +39,13 @@ async def twitter_oauth_callback( try: agent_id = state - agent = await db.get(Agent, agent_id) + agent = await Agent.get(agent_id) if not agent: raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found") - agent_data = await db.get(AgentData, agent_id) - + agent_data = await AgentData.get(agent_id) if not agent_data: agent_data = AgentData(id=agent_id) - await db.add(agent_data) # Exchange code for tokens authorization_response = ( @@ -69,7 +61,7 @@ async def twitter_oauth_callback( ) # Get user info - client = tweepy.Client(bearer_token=token["access_token"]) + client = tweepy.Client(bearer_token=token["access_token"], return_type=dict) me = client.get_me(user_auth=False) if me and "data" in me: @@ -78,8 +70,7 @@ async def twitter_oauth_callback( agent_data.twitter_name = me.get("data").get("name") # Commit changes - await db.commit() - await db.refresh(agent_data) + await agent_data.save() return JSONResponse( content={"message": "Authentication successful, you can close this window"}, diff --git a/app/services/twitter/oauth2_refresh.py b/app/services/twitter/oauth2_refresh.py index b6f052f..7cfaba1 100644 --- a/app/services/twitter/oauth2_refresh.py +++ b/app/services/twitter/oauth2_refresh.py @@ -4,7 +4,6 @@ from datetime import datetime, timedelta, timezone from sqlmodel import select -from sqlmodel.ext.asyncio.session import AsyncSession from app.services.twitter.oauth2 import oauth2_user_handler from models.agent import AgentData @@ -13,13 +12,10 @@ logger = logging.getLogger(__name__) -async def get_expiring_tokens( - db: AsyncSession, minutes_threshold: int = 10 -) -> list[AgentData]: +async def get_expiring_tokens(minutes_threshold: int = 10) -> list[AgentData]: """Get all agents with tokens expiring within the specified threshold. Args: - db: Database session minutes_threshold: Number of minutes before expiration to consider tokens as expiring Returns: @@ -29,52 +25,47 @@ async def get_expiring_tokens( minutes=minutes_threshold ) - result = await db.exec( - select(AgentData).where( - AgentData.twitter_access_token.is_not(None), - AgentData.twitter_refresh_token.is_not(None), - AgentData.twitter_access_token_expires_at <= expiration_threshold, + async with get_session() as db: + result = await db.exec( + select(AgentData).where( + AgentData.twitter_access_token.is_not(None), + AgentData.twitter_refresh_token.is_not(None), + AgentData.twitter_access_token_expires_at <= expiration_threshold, + ) ) - ) return result.all() -async def refresh_token(db: AsyncSession, agent: AgentData): +async def refresh_token(agent_data: AgentData): """Refresh Twitter OAuth2 token for an agent. Args: - db: Database session - agent: Agent data record containing refresh token + agent_data: Agent data record containing refresh token """ try: # Get new token using refresh token - token = oauth2_user_handler.refresh(agent.twitter_refresh_token) + token = oauth2_user_handler.refresh(agent_data.twitter_refresh_token) token = {} if token is None else token # Update token information - agent.twitter_access_token = token.get("access_token") - agent.twitter_refresh_token = token.get("refresh_token") + agent_data.twitter_access_token = token.get("access_token") + agent_data.twitter_refresh_token = token.get("refresh_token") if "expires_at" in token: - agent.twitter_access_token_expires_at = datetime.fromtimestamp( + agent_data.twitter_access_token_expires_at = datetime.fromtimestamp( token["expires_at"], timezone.utc ) - db.add(agent) - await db.commit() + await agent_data.save() logger.info( - f"Successfully refreshed Twitter token for agent {agent.id}, " - f"expires at {agent.twitter_access_token_expires_at}" + f"Successfully refreshed Twitter token for agent {agent_data.id}, " + f"expires at {agent_data.twitter_access_token_expires_at}" ) except Exception as e: - logger.error(f"Failed to refresh Twitter token for agent {agent.id}: {str(e)}") - # if error, reset token - agent.twitter_access_token = None - agent.twitter_refresh_token = None - agent.twitter_access_token_expires_at = None - db.add(agent) - await db.commit() + logger.error( + f"Failed to refresh Twitter token for agent {agent_data.id}: {str(e)}" + ) async def refresh_expiring_tokens(): @@ -83,7 +74,6 @@ async def refresh_expiring_tokens(): This function is designed to be called by the scheduler every minute. It will check for tokens expiring in the next 5 minutes and refresh them. """ - async with get_session() as session: - agents = await get_expiring_tokens(session) - for agent in agents: - await refresh_token(session, agent) + agents = await get_expiring_tokens() + for agent in agents: + await refresh_token(agent) diff --git a/models/skill.py b/models/skill.py index ed37298..dad2ce3 100644 --- a/models/skill.py +++ b/models/skill.py @@ -83,7 +83,7 @@ async def save(self) -> None: db.add(existing) else: db.add(self) - db.commit() + await db.commit() @classmethod async def clean_data(cls, agent_id: str): @@ -175,7 +175,7 @@ async def save(self) -> None: db.add(existing) else: db.add(self) - db.commit() + await db.commit() @classmethod async def clean_data(cls, agent_id: str, thread_id: str): diff --git a/poetry.lock b/poetry.lock index 37892da..24871da 100644 --- a/poetry.lock +++ b/poetry.lock @@ -531,13 +531,13 @@ files = [ [[package]] name = "botocore" -version = "1.36.11" +version = "1.36.12" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.36.11-py3-none-any.whl", hash = "sha256:82c5660027f696608d0e55feb08c146c11c7ebeba7615961c7765dcf6009a00d"}, - {file = "botocore-1.36.11.tar.gz", hash = "sha256:c919be883f95b9e0c3021429a365d40cd7944b8345a07af30dc8d891ceefe07a"}, + {file = "botocore-1.36.12-py3-none-any.whl", hash = "sha256:5ae1ed362c8ed908a6ced8cdd12b21e2196c100bc79f9e95c9c1fc7f9ea74f5a"}, + {file = "botocore-1.36.12.tar.gz", hash = "sha256:86ed88beb4f244c96529435c868d3940073c2774116f0023fb7691f6e7053bd9"}, ] [package.dependencies] @@ -2254,13 +2254,13 @@ dev = ["black (>=22.8.0,<22.9.0)", "flake8 (>=5.0.4,<5.1.0)", "isort (>=5.11.5,< [[package]] name = "mako" -version = "1.3.8" +version = "1.3.9" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627"}, - {file = "mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8"}, + {file = "Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1"}, + {file = "mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac"}, ] [package.dependencies] @@ -2343,13 +2343,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.26.0" +version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" files = [ - {file = "marshmallow-3.26.0-py3-none-any.whl", hash = "sha256:1287bca04e6a5f4094822ac153c03da5e214a0a60bcd557b140f3e66991b8ca1"}, - {file = "marshmallow-3.26.0.tar.gz", hash = "sha256:eb36762a1cc76d7abf831e18a3a1b26d3d481bbc74581b8e532a3d3a8115e1cb"}, + {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, + {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, ] [package.dependencies] @@ -4179,13 +4179,13 @@ anyio = ">=3.0.0" [[package]] name = "web3" -version = "7.7.0" +version = "7.8.0" description = "web3: A Python library for interacting with Ethereum" optional = false python-versions = "<4,>=3.8" files = [ - {file = "web3-7.7.0-py3-none-any.whl", hash = "sha256:2e29147c0f43cf191af788af8f004368b70994db40162f1240607f663dc68ea6"}, - {file = "web3-7.7.0.tar.gz", hash = "sha256:4d0332b9a78b855e57ccebd9e1e74c8e855b95869ac7b8fe5878731593e76408"}, + {file = "web3-7.8.0-py3-none-any.whl", hash = "sha256:c8771b3d8772f7104a0462804449beb57d36cef7bd8b411140f95a92fc46b559"}, + {file = "web3-7.8.0.tar.gz", hash = "sha256:712bc9fd6b1ef6e467ee24c25b581e1951cab2cba17f9f548f12587734f2c857"}, ] [package.dependencies] diff --git a/skills/twitter/follow_user.py b/skills/twitter/follow_user.py index 9af41be..88b88d0 100644 --- a/skills/twitter/follow_user.py +++ b/skills/twitter/follow_user.py @@ -71,7 +71,7 @@ async def _arun(self, user_id: str) -> TwitterFollowUserOutput: error=self._get_error_with_username(error), ) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterFollowUserOutput( followed=False, diff --git a/skills/twitter/get_mentions.py b/skills/twitter/get_mentions.py index b1b4ed4..e8b209c 100644 --- a/skills/twitter/get_mentions.py +++ b/skills/twitter/get_mentions.py @@ -75,7 +75,7 @@ async def _arun(self) -> TwitterGetMentionsOutput: # Always get mentions for the last day start_time = datetime.now(tz=timezone.utc) - timedelta(days=1) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterGetMentionsOutput( mentions=[], diff --git a/skills/twitter/get_timeline.py b/skills/twitter/get_timeline.py index 6fa3c1e..2c24536 100644 --- a/skills/twitter/get_timeline.py +++ b/skills/twitter/get_timeline.py @@ -83,7 +83,7 @@ async def _arun(self, max_results: int = 10) -> TwitterGetTimelineOutput: if since_id: max_results = 100 - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterGetTimelineOutput( tweets=[], diff --git a/skills/twitter/like_tweet.py b/skills/twitter/like_tweet.py index 284806d..08225b6 100644 --- a/skills/twitter/like_tweet.py +++ b/skills/twitter/like_tweet.py @@ -56,7 +56,7 @@ async def _arun(self, tweet_id: str) -> TwitterLikeTweetOutput: success=False, message=f"Error liking tweet: {error}" ) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterLikeTweetOutput( success=False, diff --git a/skills/twitter/post_tweet.py b/skills/twitter/post_tweet.py index 14776b9..f3bbde9 100644 --- a/skills/twitter/post_tweet.py +++ b/skills/twitter/post_tweet.py @@ -49,7 +49,7 @@ async def _arun(self, text: str) -> str: if is_rate_limited: return f"Error posting tweet: {error}" - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return self._get_error_with_username( "Failed to get Twitter client. Please check your authentication." diff --git a/skills/twitter/reply_tweet.py b/skills/twitter/reply_tweet.py index 935aee3..9621e17 100644 --- a/skills/twitter/reply_tweet.py +++ b/skills/twitter/reply_tweet.py @@ -51,7 +51,7 @@ async def _arun(self, tweet_id: str, text: str) -> str: f"Error replying to tweet: {error}" ) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return self._get_error_with_username( "Failed to get Twitter client. Please check your authentication." diff --git a/skills/twitter/retweet.py b/skills/twitter/retweet.py index e0587f6..7fc3a3b 100644 --- a/skills/twitter/retweet.py +++ b/skills/twitter/retweet.py @@ -56,7 +56,7 @@ async def _arun(self, tweet_id: str) -> TwitterRetweetOutput: success=False, message=f"Error retweeting: {error}" ) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterRetweetOutput( success=False, diff --git a/skills/twitter/search_tweets.py b/skills/twitter/search_tweets.py index 02cc135..b4cdc3d 100644 --- a/skills/twitter/search_tweets.py +++ b/skills/twitter/search_tweets.py @@ -63,7 +63,7 @@ async def _arun( tweets=[], error=self._get_error_with_username(error) ) - client = self.twitter.get_client() + client = await self.twitter.get_client() if not client: return TwitterSearchTweetsOutput( tweets=[],