intentkit 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +17 -0
- intentkit/abstracts/__init__.py +0 -0
- intentkit/abstracts/agent.py +60 -0
- intentkit/abstracts/api.py +4 -0
- intentkit/abstracts/engine.py +38 -0
- intentkit/abstracts/exception.py +9 -0
- intentkit/abstracts/graph.py +25 -0
- intentkit/abstracts/skill.py +129 -0
- intentkit/abstracts/twitter.py +54 -0
- intentkit/clients/__init__.py +14 -0
- intentkit/clients/cdp.py +53 -0
- intentkit/clients/twitter.py +445 -0
- intentkit/config/__init__.py +0 -0
- intentkit/config/config.py +164 -0
- intentkit/core/__init__.py +0 -0
- intentkit/core/agent.py +191 -0
- intentkit/core/api.py +40 -0
- intentkit/core/client.py +45 -0
- intentkit/core/credit.py +1767 -0
- intentkit/core/engine.py +1018 -0
- intentkit/core/node.py +223 -0
- intentkit/core/prompt.py +58 -0
- intentkit/core/skill.py +124 -0
- intentkit/models/agent.py +1689 -0
- intentkit/models/agent_data.py +810 -0
- intentkit/models/agent_schema.json +733 -0
- intentkit/models/app_setting.py +156 -0
- intentkit/models/base.py +9 -0
- intentkit/models/chat.py +581 -0
- intentkit/models/conversation.py +286 -0
- intentkit/models/credit.py +1406 -0
- intentkit/models/db.py +120 -0
- intentkit/models/db_mig.py +102 -0
- intentkit/models/generator.py +347 -0
- intentkit/models/llm.py +746 -0
- intentkit/models/redis.py +132 -0
- intentkit/models/skill.py +466 -0
- intentkit/models/user.py +243 -0
- intentkit/skills/__init__.py +12 -0
- intentkit/skills/acolyt/__init__.py +83 -0
- intentkit/skills/acolyt/acolyt.jpg +0 -0
- intentkit/skills/acolyt/ask.py +128 -0
- intentkit/skills/acolyt/base.py +28 -0
- intentkit/skills/acolyt/schema.json +89 -0
- intentkit/skills/aixbt/README.md +71 -0
- intentkit/skills/aixbt/__init__.py +73 -0
- intentkit/skills/aixbt/aixbt.jpg +0 -0
- intentkit/skills/aixbt/base.py +21 -0
- intentkit/skills/aixbt/projects.py +153 -0
- intentkit/skills/aixbt/schema.json +99 -0
- intentkit/skills/allora/__init__.py +83 -0
- intentkit/skills/allora/allora.jpeg +0 -0
- intentkit/skills/allora/base.py +28 -0
- intentkit/skills/allora/price.py +130 -0
- intentkit/skills/allora/schema.json +89 -0
- intentkit/skills/base.py +174 -0
- intentkit/skills/carv/README.md +95 -0
- intentkit/skills/carv/__init__.py +121 -0
- intentkit/skills/carv/base.py +183 -0
- intentkit/skills/carv/carv.webp +0 -0
- intentkit/skills/carv/fetch_news.py +92 -0
- intentkit/skills/carv/onchain_query.py +164 -0
- intentkit/skills/carv/schema.json +137 -0
- intentkit/skills/carv/token_info_and_price.py +110 -0
- intentkit/skills/cdp/__init__.py +137 -0
- intentkit/skills/cdp/base.py +21 -0
- intentkit/skills/cdp/cdp.png +0 -0
- intentkit/skills/cdp/get_balance.py +81 -0
- intentkit/skills/cdp/schema.json +473 -0
- intentkit/skills/chainlist/README.md +38 -0
- intentkit/skills/chainlist/__init__.py +54 -0
- intentkit/skills/chainlist/base.py +21 -0
- intentkit/skills/chainlist/chain_lookup.py +208 -0
- intentkit/skills/chainlist/chainlist.png +0 -0
- intentkit/skills/chainlist/schema.json +47 -0
- intentkit/skills/common/__init__.py +82 -0
- intentkit/skills/common/base.py +21 -0
- intentkit/skills/common/common.jpg +0 -0
- intentkit/skills/common/current_time.py +84 -0
- intentkit/skills/common/schema.json +57 -0
- intentkit/skills/cookiefun/README.md +121 -0
- intentkit/skills/cookiefun/__init__.py +78 -0
- intentkit/skills/cookiefun/base.py +41 -0
- intentkit/skills/cookiefun/constants.py +18 -0
- intentkit/skills/cookiefun/cookiefun.png +0 -0
- intentkit/skills/cookiefun/get_account_details.py +171 -0
- intentkit/skills/cookiefun/get_account_feed.py +282 -0
- intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
- intentkit/skills/cookiefun/get_sectors.py +128 -0
- intentkit/skills/cookiefun/schema.json +155 -0
- intentkit/skills/cookiefun/search_accounts.py +225 -0
- intentkit/skills/cryptocompare/__init__.py +130 -0
- intentkit/skills/cryptocompare/api.py +159 -0
- intentkit/skills/cryptocompare/base.py +303 -0
- intentkit/skills/cryptocompare/cryptocompare.png +0 -0
- intentkit/skills/cryptocompare/fetch_news.py +96 -0
- intentkit/skills/cryptocompare/fetch_price.py +99 -0
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
- intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
- intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
- intentkit/skills/cryptocompare/schema.json +168 -0
- intentkit/skills/cryptopanic/__init__.py +108 -0
- intentkit/skills/cryptopanic/base.py +51 -0
- intentkit/skills/cryptopanic/cryptopanic.png +0 -0
- intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
- intentkit/skills/cryptopanic/schema.json +103 -0
- intentkit/skills/dapplooker/README.md +92 -0
- intentkit/skills/dapplooker/__init__.py +83 -0
- intentkit/skills/dapplooker/base.py +26 -0
- intentkit/skills/dapplooker/dapplooker.jpg +0 -0
- intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
- intentkit/skills/dapplooker/schema.json +91 -0
- intentkit/skills/defillama/__init__.py +323 -0
- intentkit/skills/defillama/api.py +315 -0
- intentkit/skills/defillama/base.py +135 -0
- intentkit/skills/defillama/coins/__init__.py +0 -0
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
- intentkit/skills/defillama/coins/fetch_block.py +98 -0
- intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
- intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
- intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
- intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
- intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
- intentkit/skills/defillama/config/__init__.py +0 -0
- intentkit/skills/defillama/config/chains.py +433 -0
- intentkit/skills/defillama/defillama.jpeg +0 -0
- intentkit/skills/defillama/fees/__init__.py +0 -0
- intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
- intentkit/skills/defillama/schema.json +383 -0
- intentkit/skills/defillama/stablecoins/__init__.py +0 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
- intentkit/skills/defillama/tests/__init__.py +0 -0
- intentkit/skills/defillama/tests/api_integration.test.py +192 -0
- intentkit/skills/defillama/tests/api_unit.test.py +583 -0
- intentkit/skills/defillama/tvl/__init__.py +0 -0
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
- intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
- intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
- intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
- intentkit/skills/defillama/volumes/__init__.py +0 -0
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
- intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
- intentkit/skills/defillama/yields/__init__.py +0 -0
- intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
- intentkit/skills/defillama/yields/fetch_pools.py +126 -0
- intentkit/skills/dexscreener/__init__.py +93 -0
- intentkit/skills/dexscreener/base.py +133 -0
- intentkit/skills/dexscreener/dexscreener.png +0 -0
- intentkit/skills/dexscreener/model/__init__.py +0 -0
- intentkit/skills/dexscreener/model/search_token_response.py +82 -0
- intentkit/skills/dexscreener/schema.json +48 -0
- intentkit/skills/dexscreener/search_token.py +321 -0
- intentkit/skills/dune_analytics/__init__.py +103 -0
- intentkit/skills/dune_analytics/base.py +46 -0
- intentkit/skills/dune_analytics/dune.png +0 -0
- intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
- intentkit/skills/dune_analytics/schema.json +99 -0
- intentkit/skills/elfa/README.md +100 -0
- intentkit/skills/elfa/__init__.py +123 -0
- intentkit/skills/elfa/base.py +28 -0
- intentkit/skills/elfa/elfa.jpg +0 -0
- intentkit/skills/elfa/mention.py +504 -0
- intentkit/skills/elfa/schema.json +153 -0
- intentkit/skills/elfa/stats.py +118 -0
- intentkit/skills/elfa/tokens.py +126 -0
- intentkit/skills/enso/README.md +75 -0
- intentkit/skills/enso/__init__.py +114 -0
- intentkit/skills/enso/abi/__init__.py +0 -0
- intentkit/skills/enso/abi/approval.py +279 -0
- intentkit/skills/enso/abi/erc20.py +14 -0
- intentkit/skills/enso/abi/route.py +129 -0
- intentkit/skills/enso/base.py +44 -0
- intentkit/skills/enso/best_yield.py +286 -0
- intentkit/skills/enso/enso.jpg +0 -0
- intentkit/skills/enso/networks.py +105 -0
- intentkit/skills/enso/prices.py +93 -0
- intentkit/skills/enso/route.py +300 -0
- intentkit/skills/enso/schema.json +212 -0
- intentkit/skills/enso/tokens.py +223 -0
- intentkit/skills/enso/wallet.py +381 -0
- intentkit/skills/github/README.md +63 -0
- intentkit/skills/github/__init__.py +54 -0
- intentkit/skills/github/base.py +21 -0
- intentkit/skills/github/github.jpg +0 -0
- intentkit/skills/github/github_search.py +183 -0
- intentkit/skills/github/schema.json +59 -0
- intentkit/skills/heurist/__init__.py +143 -0
- intentkit/skills/heurist/base.py +26 -0
- intentkit/skills/heurist/heurist.png +0 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
- intentkit/skills/heurist/image_generation_braindance.py +162 -0
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
- intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
- intentkit/skills/heurist/image_generation_sdxl.py +161 -0
- intentkit/skills/heurist/schema.json +196 -0
- intentkit/skills/lifi/README.md +294 -0
- intentkit/skills/lifi/__init__.py +141 -0
- intentkit/skills/lifi/base.py +21 -0
- intentkit/skills/lifi/lifi.png +0 -0
- intentkit/skills/lifi/schema.json +89 -0
- intentkit/skills/lifi/token_execute.py +472 -0
- intentkit/skills/lifi/token_quote.py +190 -0
- intentkit/skills/lifi/utils.py +656 -0
- intentkit/skills/moralis/README.md +490 -0
- intentkit/skills/moralis/__init__.py +110 -0
- intentkit/skills/moralis/api.py +281 -0
- intentkit/skills/moralis/base.py +55 -0
- intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
- intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
- intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
- intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
- intentkit/skills/moralis/moralis.png +0 -0
- intentkit/skills/moralis/schema.json +156 -0
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +511 -0
- intentkit/skills/nation/__init__.py +62 -0
- intentkit/skills/nation/base.py +31 -0
- intentkit/skills/nation/nation.png +0 -0
- intentkit/skills/nation/nft_check.py +106 -0
- intentkit/skills/nation/schema.json +58 -0
- intentkit/skills/openai/__init__.py +107 -0
- intentkit/skills/openai/base.py +32 -0
- intentkit/skills/openai/dalle_image_generation.py +128 -0
- intentkit/skills/openai/gpt_image_generation.py +152 -0
- intentkit/skills/openai/gpt_image_to_image.py +186 -0
- intentkit/skills/openai/image_to_text.py +126 -0
- intentkit/skills/openai/openai.png +0 -0
- intentkit/skills/openai/schema.json +139 -0
- intentkit/skills/portfolio/README.md +55 -0
- intentkit/skills/portfolio/__init__.py +151 -0
- intentkit/skills/portfolio/base.py +107 -0
- intentkit/skills/portfolio/constants.py +9 -0
- intentkit/skills/portfolio/moralis.png +0 -0
- intentkit/skills/portfolio/schema.json +237 -0
- intentkit/skills/portfolio/token_balances.py +155 -0
- intentkit/skills/portfolio/wallet_approvals.py +102 -0
- intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
- intentkit/skills/portfolio/wallet_history.py +155 -0
- intentkit/skills/portfolio/wallet_net_worth.py +112 -0
- intentkit/skills/portfolio/wallet_nfts.py +139 -0
- intentkit/skills/portfolio/wallet_profitability.py +101 -0
- intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
- intentkit/skills/portfolio/wallet_stats.py +79 -0
- intentkit/skills/portfolio/wallet_swaps.py +147 -0
- intentkit/skills/skills.toml +103 -0
- intentkit/skills/slack/__init__.py +98 -0
- intentkit/skills/slack/base.py +55 -0
- intentkit/skills/slack/get_channel.py +109 -0
- intentkit/skills/slack/get_message.py +136 -0
- intentkit/skills/slack/schedule_message.py +92 -0
- intentkit/skills/slack/schema.json +135 -0
- intentkit/skills/slack/send_message.py +81 -0
- intentkit/skills/slack/slack.jpg +0 -0
- intentkit/skills/system/__init__.py +90 -0
- intentkit/skills/system/base.py +22 -0
- intentkit/skills/system/read_agent_api_key.py +87 -0
- intentkit/skills/system/regenerate_agent_api_key.py +77 -0
- intentkit/skills/system/schema.json +53 -0
- intentkit/skills/system/system.svg +76 -0
- intentkit/skills/tavily/README.md +86 -0
- intentkit/skills/tavily/__init__.py +91 -0
- intentkit/skills/tavily/base.py +27 -0
- intentkit/skills/tavily/schema.json +119 -0
- intentkit/skills/tavily/tavily.jpg +0 -0
- intentkit/skills/tavily/tavily_extract.py +147 -0
- intentkit/skills/tavily/tavily_search.py +139 -0
- intentkit/skills/token/README.md +89 -0
- intentkit/skills/token/__init__.py +107 -0
- intentkit/skills/token/base.py +154 -0
- intentkit/skills/token/constants.py +9 -0
- intentkit/skills/token/erc20_transfers.py +145 -0
- intentkit/skills/token/moralis.png +0 -0
- intentkit/skills/token/schema.json +141 -0
- intentkit/skills/token/token_analytics.py +81 -0
- intentkit/skills/token/token_price.py +132 -0
- intentkit/skills/token/token_search.py +121 -0
- intentkit/skills/twitter/__init__.py +146 -0
- intentkit/skills/twitter/base.py +68 -0
- intentkit/skills/twitter/follow_user.py +69 -0
- intentkit/skills/twitter/get_mentions.py +124 -0
- intentkit/skills/twitter/get_timeline.py +111 -0
- intentkit/skills/twitter/get_user_by_username.py +84 -0
- intentkit/skills/twitter/get_user_tweets.py +123 -0
- intentkit/skills/twitter/like_tweet.py +65 -0
- intentkit/skills/twitter/post_tweet.py +90 -0
- intentkit/skills/twitter/reply_tweet.py +98 -0
- intentkit/skills/twitter/retweet.py +76 -0
- intentkit/skills/twitter/schema.json +258 -0
- intentkit/skills/twitter/search_tweets.py +115 -0
- intentkit/skills/twitter/twitter.png +0 -0
- intentkit/skills/unrealspeech/__init__.py +55 -0
- intentkit/skills/unrealspeech/base.py +21 -0
- intentkit/skills/unrealspeech/schema.json +100 -0
- intentkit/skills/unrealspeech/text_to_speech.py +177 -0
- intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
- intentkit/skills/venice_audio/__init__.py +106 -0
- intentkit/skills/venice_audio/base.py +119 -0
- intentkit/skills/venice_audio/input.py +41 -0
- intentkit/skills/venice_audio/schema.json +152 -0
- intentkit/skills/venice_audio/venice_audio.py +240 -0
- intentkit/skills/venice_audio/venice_logo.jpg +0 -0
- intentkit/skills/venice_image/README.md +119 -0
- intentkit/skills/venice_image/__init__.py +154 -0
- intentkit/skills/venice_image/api.py +138 -0
- intentkit/skills/venice_image/base.py +188 -0
- intentkit/skills/venice_image/config.py +35 -0
- intentkit/skills/venice_image/image_enhance/README.md +119 -0
- intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
- intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
- intentkit/skills/venice_image/image_generation/README.md +144 -0
- intentkit/skills/venice_image/image_generation/__init__.py +0 -0
- intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
- intentkit/skills/venice_image/image_upscale/README.md +111 -0
- intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
- intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
- intentkit/skills/venice_image/image_vision/README.md +112 -0
- intentkit/skills/venice_image/image_vision/__init__.py +0 -0
- intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
- intentkit/skills/venice_image/schema.json +267 -0
- intentkit/skills/venice_image/utils.py +78 -0
- intentkit/skills/venice_image/venice_image.jpg +0 -0
- intentkit/skills/web_scraper/README.md +82 -0
- intentkit/skills/web_scraper/__init__.py +92 -0
- intentkit/skills/web_scraper/base.py +21 -0
- intentkit/skills/web_scraper/langchain.png +0 -0
- intentkit/skills/web_scraper/schema.json +115 -0
- intentkit/skills/web_scraper/scrape_and_index.py +327 -0
- intentkit/utils/__init__.py +1 -0
- intentkit/utils/chain.py +436 -0
- intentkit/utils/error.py +134 -0
- intentkit/utils/logging.py +70 -0
- intentkit/utils/middleware.py +61 -0
- intentkit/utils/random.py +16 -0
- intentkit/utils/s3.py +267 -0
- intentkit/utils/slack_alert.py +79 -0
- intentkit/utils/tx.py +37 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
- intentkit-0.5.2.dist-info/RECORD +365 -0
- intentkit-0.5.0.dist-info/RECORD +0 -4
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from intentkit.skills.cookiefun.base import CookieFunBaseTool, logger
|
|
8
|
+
from intentkit.skills.cookiefun.constants import DEFAULT_HEADERS, ENDPOINTS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GetAccountSmartFollowersInput(BaseModel):
|
|
12
|
+
"""Input for GetAccountSmartFollowers tool."""
|
|
13
|
+
|
|
14
|
+
username: Optional[str] = Field(
|
|
15
|
+
default=None,
|
|
16
|
+
description="Twitter username (either username or userId is required)",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
userId: Optional[str] = Field(
|
|
20
|
+
default=None,
|
|
21
|
+
description="Twitter user ID (either username or userId is required)",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GetAccountSmartFollowers(CookieFunBaseTool):
|
|
26
|
+
"""Tool to get smart followers for a Twitter account."""
|
|
27
|
+
|
|
28
|
+
name: str = "cookiefun_get_account_smart_followers"
|
|
29
|
+
description: str = "Returns a list of top smart followers for a specific Twitter account, with detailed metrics about these followers."
|
|
30
|
+
args_schema: Type[BaseModel] = GetAccountSmartFollowersInput
|
|
31
|
+
|
|
32
|
+
async def _arun(
|
|
33
|
+
self,
|
|
34
|
+
config: RunnableConfig,
|
|
35
|
+
username: Optional[str] = None,
|
|
36
|
+
userId: Optional[str] = None,
|
|
37
|
+
**kwargs,
|
|
38
|
+
) -> Union[List[Dict[str, Any]], str]:
|
|
39
|
+
"""
|
|
40
|
+
Get smart followers for a Twitter account.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
username: Twitter username (either username or userId is required)
|
|
44
|
+
userId: Twitter user ID (either username or userId is required)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
List of top smart followers with their metrics.
|
|
48
|
+
"""
|
|
49
|
+
logger.info(
|
|
50
|
+
"Getting smart followers for username=%s, userId=%s", username, userId
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Validate input parameters
|
|
54
|
+
if not username and not userId:
|
|
55
|
+
logger.error("Neither username nor userId provided")
|
|
56
|
+
return "Error: Either username or userId must be provided."
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Get context to retrieve API key
|
|
60
|
+
api_key = self.get_api_key(config)
|
|
61
|
+
|
|
62
|
+
if not api_key:
|
|
63
|
+
logger.error("No API key provided for CookieFun API")
|
|
64
|
+
return "Error: No API key provided for CookieFun API. Please configure the API key in the agent settings."
|
|
65
|
+
|
|
66
|
+
# Prepare request payload
|
|
67
|
+
payload = {}
|
|
68
|
+
if username:
|
|
69
|
+
payload["username"] = username
|
|
70
|
+
if userId:
|
|
71
|
+
payload["userId"] = userId
|
|
72
|
+
|
|
73
|
+
# Make API request
|
|
74
|
+
headers = {**DEFAULT_HEADERS, "x-api-key": api_key}
|
|
75
|
+
|
|
76
|
+
async with httpx.AsyncClient() as client:
|
|
77
|
+
response = await client.post(
|
|
78
|
+
ENDPOINTS["smart_followers"], headers=headers, json=payload
|
|
79
|
+
)
|
|
80
|
+
logger.debug(
|
|
81
|
+
"Received response with status code: %d", response.status_code
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
data = response.json()
|
|
86
|
+
|
|
87
|
+
# Check different possible response structures
|
|
88
|
+
if data.get("success") and "ok" in data and "entries" in data["ok"]:
|
|
89
|
+
followers = data["ok"]["entries"]
|
|
90
|
+
logger.info(
|
|
91
|
+
"Successfully retrieved %d smart followers from entries field",
|
|
92
|
+
len(followers),
|
|
93
|
+
)
|
|
94
|
+
return followers
|
|
95
|
+
elif data.get("success") and "ok" in data and "accounts" in data["ok"]:
|
|
96
|
+
followers = data["ok"]["accounts"]
|
|
97
|
+
logger.info(
|
|
98
|
+
"Successfully retrieved %d smart followers", len(followers)
|
|
99
|
+
)
|
|
100
|
+
return followers
|
|
101
|
+
elif data.get("success") and "ok" in data and "followers" in data["ok"]:
|
|
102
|
+
followers = data["ok"]["followers"]
|
|
103
|
+
logger.info(
|
|
104
|
+
"Successfully retrieved %d smart followers from followers field",
|
|
105
|
+
len(followers),
|
|
106
|
+
)
|
|
107
|
+
return followers
|
|
108
|
+
elif (
|
|
109
|
+
data.get("success")
|
|
110
|
+
and "ok" in data
|
|
111
|
+
and isinstance(data["ok"], list)
|
|
112
|
+
):
|
|
113
|
+
followers = data["ok"]
|
|
114
|
+
logger.info(
|
|
115
|
+
"Successfully retrieved %d smart followers from ok list",
|
|
116
|
+
len(followers),
|
|
117
|
+
)
|
|
118
|
+
return followers
|
|
119
|
+
elif data.get("success") and isinstance(data.get("accounts"), list):
|
|
120
|
+
followers = data["accounts"]
|
|
121
|
+
logger.info(
|
|
122
|
+
"Successfully retrieved %d smart followers from top level accounts",
|
|
123
|
+
len(followers),
|
|
124
|
+
)
|
|
125
|
+
return followers
|
|
126
|
+
elif data.get("success") and isinstance(data.get("followers"), list):
|
|
127
|
+
followers = data["followers"]
|
|
128
|
+
logger.info(
|
|
129
|
+
"Successfully retrieved %d smart followers from top level followers",
|
|
130
|
+
len(followers),
|
|
131
|
+
)
|
|
132
|
+
return followers
|
|
133
|
+
elif data.get("success") and isinstance(data.get("entries"), list):
|
|
134
|
+
followers = data["entries"]
|
|
135
|
+
logger.info(
|
|
136
|
+
"Successfully retrieved %d smart followers from top level entries",
|
|
137
|
+
len(followers),
|
|
138
|
+
)
|
|
139
|
+
return followers
|
|
140
|
+
elif "followers" in data and isinstance(data["followers"], list):
|
|
141
|
+
followers = data["followers"]
|
|
142
|
+
logger.info(
|
|
143
|
+
"Successfully retrieved %d smart followers from direct followers field",
|
|
144
|
+
len(followers),
|
|
145
|
+
)
|
|
146
|
+
return followers
|
|
147
|
+
elif "accounts" in data and isinstance(data["accounts"], list):
|
|
148
|
+
followers = data["accounts"]
|
|
149
|
+
logger.info(
|
|
150
|
+
"Successfully retrieved %d smart followers from direct accounts field",
|
|
151
|
+
len(followers),
|
|
152
|
+
)
|
|
153
|
+
return followers
|
|
154
|
+
elif "entries" in data and isinstance(data["entries"], list):
|
|
155
|
+
followers = data["entries"]
|
|
156
|
+
logger.info(
|
|
157
|
+
"Successfully retrieved %d smart followers from direct entries field",
|
|
158
|
+
len(followers),
|
|
159
|
+
)
|
|
160
|
+
return followers
|
|
161
|
+
else:
|
|
162
|
+
# If we can't find followers in the expected structure, log the full response
|
|
163
|
+
logger.error(
|
|
164
|
+
"Could not find smart followers in response structure. Full response: %s",
|
|
165
|
+
data,
|
|
166
|
+
)
|
|
167
|
+
error_msg = data.get(
|
|
168
|
+
"error", "Unknown error - check API response format"
|
|
169
|
+
)
|
|
170
|
+
logger.error("Error in API response: %s", error_msg)
|
|
171
|
+
return f"Error fetching smart followers: {error_msg}"
|
|
172
|
+
|
|
173
|
+
except httpx.HTTPStatusError as e:
|
|
174
|
+
logger.error("HTTP error: %d - %s", e.response.status_code, e.response.text)
|
|
175
|
+
return f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
|
|
176
|
+
except httpx.RequestError as e:
|
|
177
|
+
logger.error("Request error: %s", str(e))
|
|
178
|
+
return f"Request error occurred: {str(e)}"
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.exception("Unexpected error occurred")
|
|
181
|
+
return f"An unexpected error occurred: {str(e)}"
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Type
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from intentkit.skills.cookiefun.base import CookieFunBaseTool, logger
|
|
8
|
+
from intentkit.skills.cookiefun.constants import DEFAULT_HEADERS, ENDPOINTS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GetSectorsInput(BaseModel):
|
|
12
|
+
"""Input for GetSectors tool."""
|
|
13
|
+
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GetSectors(CookieFunBaseTool):
|
|
18
|
+
"""Tool to get all available sectors from the CookieFun API."""
|
|
19
|
+
|
|
20
|
+
name: str = "cookiefun_get_sectors"
|
|
21
|
+
description: str = (
|
|
22
|
+
"Returns a list of all available sectors in the CookieFun system."
|
|
23
|
+
)
|
|
24
|
+
args_schema: Type[BaseModel] = GetSectorsInput
|
|
25
|
+
|
|
26
|
+
async def _arun(self, config: RunnableConfig, **kwargs) -> List[Dict[str, Any]]:
|
|
27
|
+
"""
|
|
28
|
+
Get all available sectors from the CookieFun API.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
List of sector objects, each containing id, name, and other metadata.
|
|
32
|
+
"""
|
|
33
|
+
logger.info("Getting sectors from CookieFun API")
|
|
34
|
+
try:
|
|
35
|
+
# Get API key
|
|
36
|
+
api_key = self.get_api_key(config)
|
|
37
|
+
|
|
38
|
+
if not api_key:
|
|
39
|
+
logger.error("No API key provided for CookieFun API")
|
|
40
|
+
return "Error: No API key provided for CookieFun API. Please configure the API key in the agent settings."
|
|
41
|
+
|
|
42
|
+
# Make API request
|
|
43
|
+
headers = {**DEFAULT_HEADERS, "x-api-key": api_key}
|
|
44
|
+
|
|
45
|
+
async with httpx.AsyncClient() as client:
|
|
46
|
+
response = await client.get(ENDPOINTS["sectors"], headers=headers)
|
|
47
|
+
logger.debug(
|
|
48
|
+
"Received response with status code: %d", response.status_code
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
response.raise_for_status()
|
|
52
|
+
data = response.json()
|
|
53
|
+
|
|
54
|
+
# Check different possible response structures
|
|
55
|
+
if data.get("success") and "ok" in data and "entries" in data["ok"]:
|
|
56
|
+
sectors = data["ok"]["entries"]
|
|
57
|
+
logger.info(
|
|
58
|
+
"Successfully retrieved %d sectors from entries field",
|
|
59
|
+
len(sectors),
|
|
60
|
+
)
|
|
61
|
+
return sectors
|
|
62
|
+
elif data.get("success") and "ok" in data and "sectors" in data["ok"]:
|
|
63
|
+
sectors = data["ok"]["sectors"]
|
|
64
|
+
logger.info("Successfully retrieved %d sectors", len(sectors))
|
|
65
|
+
return sectors
|
|
66
|
+
elif (
|
|
67
|
+
data.get("success")
|
|
68
|
+
and "ok" in data
|
|
69
|
+
and isinstance(data["ok"], list)
|
|
70
|
+
):
|
|
71
|
+
# If "ok" is directly a list
|
|
72
|
+
sectors = data["ok"]
|
|
73
|
+
logger.info(
|
|
74
|
+
"Successfully retrieved %d sectors from ok list", len(sectors)
|
|
75
|
+
)
|
|
76
|
+
return sectors
|
|
77
|
+
elif data.get("success") and isinstance(data.get("sectors"), list):
|
|
78
|
+
# If sectors is at the top level
|
|
79
|
+
sectors = data["sectors"]
|
|
80
|
+
logger.info(
|
|
81
|
+
"Successfully retrieved %d sectors from top level", len(sectors)
|
|
82
|
+
)
|
|
83
|
+
return sectors
|
|
84
|
+
elif data.get("success") and isinstance(data.get("entries"), list):
|
|
85
|
+
# If entries is at the top level
|
|
86
|
+
sectors = data["entries"]
|
|
87
|
+
logger.info(
|
|
88
|
+
"Successfully retrieved %d sectors from entries top level",
|
|
89
|
+
len(sectors),
|
|
90
|
+
)
|
|
91
|
+
return sectors
|
|
92
|
+
elif "sectors" in data and isinstance(data["sectors"], list):
|
|
93
|
+
# If only sectors field exists
|
|
94
|
+
sectors = data["sectors"]
|
|
95
|
+
logger.info(
|
|
96
|
+
"Successfully retrieved %d sectors from direct field",
|
|
97
|
+
len(sectors),
|
|
98
|
+
)
|
|
99
|
+
return sectors
|
|
100
|
+
elif "entries" in data and isinstance(data["entries"], list):
|
|
101
|
+
# If only entries field exists
|
|
102
|
+
sectors = data["entries"]
|
|
103
|
+
logger.info(
|
|
104
|
+
"Successfully retrieved %d sectors from direct entries field",
|
|
105
|
+
len(sectors),
|
|
106
|
+
)
|
|
107
|
+
return sectors
|
|
108
|
+
else:
|
|
109
|
+
# If we can't find sectors in the expected structure, log the full response for debugging
|
|
110
|
+
logger.error(
|
|
111
|
+
"Could not find sectors in response structure. Full response: %s",
|
|
112
|
+
data,
|
|
113
|
+
)
|
|
114
|
+
error_msg = data.get(
|
|
115
|
+
"error", "Unknown error - check API response format"
|
|
116
|
+
)
|
|
117
|
+
logger.error("Error in API response: %s", error_msg)
|
|
118
|
+
return f"Error fetching sectors: {error_msg}"
|
|
119
|
+
|
|
120
|
+
except httpx.HTTPStatusError as e:
|
|
121
|
+
logger.error("HTTP error: %d - %s", e.response.status_code, e.response.text)
|
|
122
|
+
return f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
|
|
123
|
+
except httpx.RequestError as e:
|
|
124
|
+
logger.error("Request error: %s", str(e))
|
|
125
|
+
return f"Request error occurred: {str(e)}"
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.exception("Unexpected error occurred")
|
|
128
|
+
return f"An unexpected error occurred: {str(e)}"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "CookieFun Skills",
|
|
5
|
+
"description": "Access Twitter/X analytics and insights using CookieFun API. Get data about accounts, tweets, followers, and trends across different industry sectors.",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/cookiefun/cookiefun.png",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"Twitter",
|
|
9
|
+
"Social Media",
|
|
10
|
+
"Analytics",
|
|
11
|
+
"X"
|
|
12
|
+
],
|
|
13
|
+
"x-nft-requirement": 10,
|
|
14
|
+
"properties": {
|
|
15
|
+
"enabled": {
|
|
16
|
+
"type": "boolean",
|
|
17
|
+
"title": "Enabled",
|
|
18
|
+
"description": "Toggle to enable or disable all CookieFun skills",
|
|
19
|
+
"default": false
|
|
20
|
+
},
|
|
21
|
+
"states": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"get_sectors": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"title": "Get Sectors",
|
|
27
|
+
"enum": [
|
|
28
|
+
"disabled",
|
|
29
|
+
"public",
|
|
30
|
+
"private"
|
|
31
|
+
],
|
|
32
|
+
"x-enum-title": [
|
|
33
|
+
"Disabled",
|
|
34
|
+
"Agent Owner + All Users",
|
|
35
|
+
"Agent Owner Only"
|
|
36
|
+
],
|
|
37
|
+
"description": "Retrieve a list of all available industry sectors in CookieFun, useful for exploring trending topics and categorization",
|
|
38
|
+
"default": "disabled"
|
|
39
|
+
},
|
|
40
|
+
"get_account_details": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"title": "Get Account Details",
|
|
43
|
+
"enum": [
|
|
44
|
+
"disabled",
|
|
45
|
+
"public",
|
|
46
|
+
"private"
|
|
47
|
+
],
|
|
48
|
+
"x-enum-title": [
|
|
49
|
+
"Disabled",
|
|
50
|
+
"Agent Owner + All Users",
|
|
51
|
+
"Agent Owner Only"
|
|
52
|
+
],
|
|
53
|
+
"description": "Fetch comprehensive metrics about any Twitter account including followers, engagement rates, impressions, and other analytics",
|
|
54
|
+
"default": "disabled"
|
|
55
|
+
},
|
|
56
|
+
"get_account_smart_followers": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"title": "Get Account Smart Followers",
|
|
59
|
+
"enum": [
|
|
60
|
+
"disabled",
|
|
61
|
+
"public",
|
|
62
|
+
"private"
|
|
63
|
+
],
|
|
64
|
+
"x-enum-title": [
|
|
65
|
+
"Disabled",
|
|
66
|
+
"Agent Owner + All Users",
|
|
67
|
+
"Agent Owner Only"
|
|
68
|
+
],
|
|
69
|
+
"description": "Identify the most valuable followers of any Twitter account based on influence, engagement, and reach metrics",
|
|
70
|
+
"default": "disabled"
|
|
71
|
+
},
|
|
72
|
+
"search_accounts": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"title": "Search Accounts",
|
|
75
|
+
"enum": [
|
|
76
|
+
"disabled",
|
|
77
|
+
"public",
|
|
78
|
+
"private"
|
|
79
|
+
],
|
|
80
|
+
"x-enum-title": [
|
|
81
|
+
"Disabled",
|
|
82
|
+
"Agent Owner + All Users",
|
|
83
|
+
"Agent Owner Only"
|
|
84
|
+
],
|
|
85
|
+
"description": "Find Twitter accounts posting about specific topics with filtering by engagement, impressions, and tweet types",
|
|
86
|
+
"default": "disabled"
|
|
87
|
+
},
|
|
88
|
+
"get_account_feed": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"title": "Get Account Feed",
|
|
91
|
+
"enum": [
|
|
92
|
+
"disabled",
|
|
93
|
+
"public",
|
|
94
|
+
"private"
|
|
95
|
+
],
|
|
96
|
+
"x-enum-title": [
|
|
97
|
+
"Disabled",
|
|
98
|
+
"Agent Owner + All Users",
|
|
99
|
+
"Agent Owner Only"
|
|
100
|
+
],
|
|
101
|
+
"description": "Access a Twitter account's feed with powerful filtering by date range, media content, tweet type, and sorting options",
|
|
102
|
+
"default": "disabled"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"description": "Configure access levels for each CookieFun skill - disabled, available to all users, or restricted to agent owner only"
|
|
106
|
+
},
|
|
107
|
+
"api_key_provider": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"title": "API Key Provider",
|
|
110
|
+
"description": "Choose whether to use a platform-provided API key or provide your own CookieFun API key",
|
|
111
|
+
"enum": [
|
|
112
|
+
"agent_owner"
|
|
113
|
+
],
|
|
114
|
+
"x-enum-title": [
|
|
115
|
+
"Owner Provided"
|
|
116
|
+
],
|
|
117
|
+
"default": "agent_owner"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"required": [
|
|
121
|
+
"states",
|
|
122
|
+
"enabled"
|
|
123
|
+
],
|
|
124
|
+
"if": {
|
|
125
|
+
"properties": {
|
|
126
|
+
"api_key_provider": {
|
|
127
|
+
"const": "agent_owner"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"then": {
|
|
132
|
+
"properties": {
|
|
133
|
+
"api_key": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"title": "CookieFun API Key",
|
|
136
|
+
"description": "Your personal CookieFun API key, required when using Owner Provided option (sign up at cookie.fun)",
|
|
137
|
+
"x-sensitive": true,
|
|
138
|
+
"x-link": "[Get your API key](https://cookie.fun/)"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"if": {
|
|
142
|
+
"properties": {
|
|
143
|
+
"enabled": {
|
|
144
|
+
"const": true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"then": {
|
|
149
|
+
"required": [
|
|
150
|
+
"api_key"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"additionalProperties": true
|
|
155
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from langchain_core.runnables import RunnableConfig
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from intentkit.skills.cookiefun.base import CookieFunBaseTool, logger
|
|
9
|
+
from intentkit.skills.cookiefun.constants import DEFAULT_HEADERS, ENDPOINTS
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TweetType(IntEnum):
|
|
13
|
+
"""Tweet type for filtering."""
|
|
14
|
+
|
|
15
|
+
Original = 0
|
|
16
|
+
Reply = 1
|
|
17
|
+
Quote = 2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SortBy(IntEnum):
|
|
21
|
+
"""Sort options for account search results."""
|
|
22
|
+
|
|
23
|
+
SmartEngagementPoints = 0
|
|
24
|
+
Impressions = 1
|
|
25
|
+
MatchingTweetsCount = 2
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SortOrder(IntEnum):
|
|
29
|
+
"""Sort order options."""
|
|
30
|
+
|
|
31
|
+
Ascending = 0
|
|
32
|
+
Descending = 1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SearchAccountsInput(BaseModel):
|
|
36
|
+
"""Input for SearchAccounts tool."""
|
|
37
|
+
|
|
38
|
+
searchQuery: str = Field(
|
|
39
|
+
description="Search query to find Twitter accounts that authored tweets matching the criteria"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
type: Optional[int] = Field(
|
|
43
|
+
default=None,
|
|
44
|
+
description="Type of tweets to search for: 0 for Original, 1 for Reply, 2 for Quote (leave empty for all types)",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
sortBy: Optional[int] = Field(
|
|
48
|
+
default=None,
|
|
49
|
+
description="Sort by: 0 for SmartEngagementPoints, 1 for Impressions, 2 for MatchingTweetsCount",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
sortOrder: Optional[int] = Field(
|
|
53
|
+
default=None,
|
|
54
|
+
description="Sort order: 0 for Ascending, 1 for Descending",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SearchAccounts(CookieFunBaseTool):
|
|
59
|
+
"""Tool to search for Twitter accounts based on tweet content."""
|
|
60
|
+
|
|
61
|
+
name: str = "cookiefun_search_accounts"
|
|
62
|
+
description: str = "Searches for Twitter accounts that authored tweets matching specified search criteria."
|
|
63
|
+
args_schema: Type[BaseModel] = SearchAccountsInput
|
|
64
|
+
|
|
65
|
+
async def _arun(
|
|
66
|
+
self,
|
|
67
|
+
config: RunnableConfig,
|
|
68
|
+
searchQuery: str,
|
|
69
|
+
type: Optional[int] = None,
|
|
70
|
+
sortBy: Optional[int] = None,
|
|
71
|
+
sortOrder: Optional[int] = None,
|
|
72
|
+
**kwargs,
|
|
73
|
+
) -> Union[List[Dict[str, Any]], str]:
|
|
74
|
+
"""
|
|
75
|
+
Search for Twitter accounts based on tweet content.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
searchQuery: Search query to match tweet content
|
|
79
|
+
type: Type of tweets to search for (0=Original, 1=Reply, 2=Quote)
|
|
80
|
+
sortBy: Sort by field (0=SmartEngagementPoints, 1=Impressions, 2=MatchingTweetsCount)
|
|
81
|
+
sortOrder: Sort order (0=Ascending, 1=Descending)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of Twitter accounts matching the search criteria with metrics.
|
|
85
|
+
"""
|
|
86
|
+
logger.info(
|
|
87
|
+
"Searching accounts with query=%s, type=%s, sortBy=%s, sortOrder=%s",
|
|
88
|
+
searchQuery,
|
|
89
|
+
type,
|
|
90
|
+
sortBy,
|
|
91
|
+
sortOrder,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if not searchQuery:
|
|
95
|
+
logger.error("No search query provided")
|
|
96
|
+
return "Error: searchQuery is required."
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
# Get context to retrieve API key
|
|
100
|
+
api_key = self.get_api_key(config)
|
|
101
|
+
|
|
102
|
+
if not api_key:
|
|
103
|
+
logger.error("No API key provided for CookieFun API")
|
|
104
|
+
return "Error: No API key provided for CookieFun API. Please configure the API key in the agent settings."
|
|
105
|
+
|
|
106
|
+
# Prepare request payload
|
|
107
|
+
payload = {"searchQuery": searchQuery}
|
|
108
|
+
|
|
109
|
+
# Add optional parameters if provided
|
|
110
|
+
if type is not None:
|
|
111
|
+
payload["type"] = type
|
|
112
|
+
if sortBy is not None:
|
|
113
|
+
payload["sortBy"] = sortBy
|
|
114
|
+
if sortOrder is not None:
|
|
115
|
+
payload["sortOrder"] = sortOrder
|
|
116
|
+
|
|
117
|
+
# Make API request
|
|
118
|
+
headers = {**DEFAULT_HEADERS, "x-api-key": api_key}
|
|
119
|
+
|
|
120
|
+
async with httpx.AsyncClient() as client:
|
|
121
|
+
response = await client.post(
|
|
122
|
+
ENDPOINTS["search_accounts"], headers=headers, json=payload
|
|
123
|
+
)
|
|
124
|
+
logger.debug(
|
|
125
|
+
"Received response with status code: %d", response.status_code
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
response.raise_for_status()
|
|
129
|
+
data = response.json()
|
|
130
|
+
|
|
131
|
+
# Check different possible response structures
|
|
132
|
+
if data.get("success") and "ok" in data and "entries" in data["ok"]:
|
|
133
|
+
accounts = data["ok"]["entries"]
|
|
134
|
+
logger.info(
|
|
135
|
+
"Successfully retrieved %d matching accounts from entries field",
|
|
136
|
+
len(accounts),
|
|
137
|
+
)
|
|
138
|
+
return accounts
|
|
139
|
+
elif data.get("success") and "ok" in data and "accounts" in data["ok"]:
|
|
140
|
+
accounts = data["ok"]["accounts"]
|
|
141
|
+
logger.info(
|
|
142
|
+
"Successfully retrieved %d matching accounts", len(accounts)
|
|
143
|
+
)
|
|
144
|
+
return accounts
|
|
145
|
+
elif data.get("success") and "ok" in data and "results" in data["ok"]:
|
|
146
|
+
accounts = data["ok"]["results"]
|
|
147
|
+
logger.info(
|
|
148
|
+
"Successfully retrieved %d matching accounts from results field",
|
|
149
|
+
len(accounts),
|
|
150
|
+
)
|
|
151
|
+
return accounts
|
|
152
|
+
elif (
|
|
153
|
+
data.get("success")
|
|
154
|
+
and "ok" in data
|
|
155
|
+
and isinstance(data["ok"], list)
|
|
156
|
+
):
|
|
157
|
+
accounts = data["ok"]
|
|
158
|
+
logger.info(
|
|
159
|
+
"Successfully retrieved %d matching accounts from ok list",
|
|
160
|
+
len(accounts),
|
|
161
|
+
)
|
|
162
|
+
return accounts
|
|
163
|
+
elif data.get("success") and isinstance(data.get("accounts"), list):
|
|
164
|
+
accounts = data["accounts"]
|
|
165
|
+
logger.info(
|
|
166
|
+
"Successfully retrieved %d matching accounts from top level accounts",
|
|
167
|
+
len(accounts),
|
|
168
|
+
)
|
|
169
|
+
return accounts
|
|
170
|
+
elif data.get("success") and isinstance(data.get("results"), list):
|
|
171
|
+
accounts = data["results"]
|
|
172
|
+
logger.info(
|
|
173
|
+
"Successfully retrieved %d matching accounts from top level results",
|
|
174
|
+
len(accounts),
|
|
175
|
+
)
|
|
176
|
+
return accounts
|
|
177
|
+
elif data.get("success") and isinstance(data.get("entries"), list):
|
|
178
|
+
accounts = data["entries"]
|
|
179
|
+
logger.info(
|
|
180
|
+
"Successfully retrieved %d matching accounts from top level entries",
|
|
181
|
+
len(accounts),
|
|
182
|
+
)
|
|
183
|
+
return accounts
|
|
184
|
+
elif "accounts" in data and isinstance(data["accounts"], list):
|
|
185
|
+
accounts = data["accounts"]
|
|
186
|
+
logger.info(
|
|
187
|
+
"Successfully retrieved %d matching accounts from direct accounts field",
|
|
188
|
+
len(accounts),
|
|
189
|
+
)
|
|
190
|
+
return accounts
|
|
191
|
+
elif "results" in data and isinstance(data["results"], list):
|
|
192
|
+
accounts = data["results"]
|
|
193
|
+
logger.info(
|
|
194
|
+
"Successfully retrieved %d matching accounts from direct results field",
|
|
195
|
+
len(accounts),
|
|
196
|
+
)
|
|
197
|
+
return accounts
|
|
198
|
+
elif "entries" in data and isinstance(data["entries"], list):
|
|
199
|
+
accounts = data["entries"]
|
|
200
|
+
logger.info(
|
|
201
|
+
"Successfully retrieved %d matching accounts from direct entries field",
|
|
202
|
+
len(accounts),
|
|
203
|
+
)
|
|
204
|
+
return accounts
|
|
205
|
+
else:
|
|
206
|
+
# If we can't find accounts in the expected structure, log the full response
|
|
207
|
+
logger.error(
|
|
208
|
+
"Could not find matching accounts in response structure. Full response: %s",
|
|
209
|
+
data,
|
|
210
|
+
)
|
|
211
|
+
error_msg = data.get(
|
|
212
|
+
"error", "Unknown error - check API response format"
|
|
213
|
+
)
|
|
214
|
+
logger.error("Error in API response: %s", error_msg)
|
|
215
|
+
return f"Error searching accounts: {error_msg}"
|
|
216
|
+
|
|
217
|
+
except httpx.HTTPStatusError as e:
|
|
218
|
+
logger.error("HTTP error: %d - %s", e.response.status_code, e.response.text)
|
|
219
|
+
return f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
|
|
220
|
+
except httpx.RequestError as e:
|
|
221
|
+
logger.error("Request error: %s", str(e))
|
|
222
|
+
return f"Request error occurred: {str(e)}"
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.exception("Unexpected error occurred")
|
|
225
|
+
return f"An unexpected error occurred: {str(e)}"
|