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,445 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
5
|
+
from typing import Any, Dict, List, NotRequired, Optional, TypedDict
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
from tweepy.asynchronous import AsyncClient
|
|
10
|
+
|
|
11
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
12
|
+
from intentkit.abstracts.twitter import TwitterABC
|
|
13
|
+
from intentkit.models.agent_data import AgentData
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_clients_linked: Dict[str, "TwitterClient"] = {}
|
|
18
|
+
_clients_self_key: Dict[str, "TwitterClient"] = {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TwitterMedia(BaseModel):
|
|
22
|
+
"""Model representing Twitter media from the API response."""
|
|
23
|
+
|
|
24
|
+
media_key: str
|
|
25
|
+
type: str
|
|
26
|
+
url: Optional[str] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TwitterUser(BaseModel):
|
|
30
|
+
"""Model representing a Twitter user from the API response."""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
name: str
|
|
34
|
+
username: str
|
|
35
|
+
description: str
|
|
36
|
+
public_metrics: dict = Field(
|
|
37
|
+
description="User metrics including followers_count, following_count, tweet_count, listed_count, like_count, and media_count"
|
|
38
|
+
)
|
|
39
|
+
is_following: bool = Field(
|
|
40
|
+
description="Whether the authenticated user is following this user",
|
|
41
|
+
default=False,
|
|
42
|
+
)
|
|
43
|
+
is_follower: bool = Field(
|
|
44
|
+
description="Whether this user is following the authenticated user",
|
|
45
|
+
default=False,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Tweet(BaseModel):
|
|
50
|
+
"""Model representing a Twitter tweet."""
|
|
51
|
+
|
|
52
|
+
id: str
|
|
53
|
+
text: str
|
|
54
|
+
author_id: str
|
|
55
|
+
author: Optional[TwitterUser] = None
|
|
56
|
+
created_at: datetime
|
|
57
|
+
referenced_tweets: Optional[List["Tweet"]] = None
|
|
58
|
+
attachments: Optional[List[TwitterMedia]] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TwitterClientConfig(TypedDict):
|
|
62
|
+
consumer_key: NotRequired[str]
|
|
63
|
+
consumer_secret: NotRequired[str]
|
|
64
|
+
access_token: NotRequired[str]
|
|
65
|
+
access_token_secret: NotRequired[str]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TwitterClient(TwitterABC):
|
|
69
|
+
"""Implementation of Twitter operations using Tweepy client.
|
|
70
|
+
|
|
71
|
+
This class provides concrete implementations for interacting with Twitter's API
|
|
72
|
+
through a Tweepy client, supporting both API key and OAuth2 authentication.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
agent_id: The ID of the agent
|
|
76
|
+
skill_store: The skill store for retrieving data
|
|
77
|
+
config: Configuration dictionary that may contain API keys
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, agent_id: str, skill_store: SkillStoreABC, config: Dict) -> None:
|
|
81
|
+
"""Initialize the Twitter client.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
agent_id: The ID of the agent
|
|
85
|
+
skill_store: The skill store for retrieving data
|
|
86
|
+
config: Configuration dictionary that may contain API keys
|
|
87
|
+
"""
|
|
88
|
+
self.agent_id = agent_id
|
|
89
|
+
self._client: Optional[AsyncClient] = None
|
|
90
|
+
self._skill_store = skill_store
|
|
91
|
+
self._agent_data: Optional[AgentData] = None
|
|
92
|
+
self.use_key = _is_self_key(config)
|
|
93
|
+
self._config = config
|
|
94
|
+
|
|
95
|
+
async def get_client(self) -> AsyncClient:
|
|
96
|
+
"""Get the initialized Twitter client.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
AsyncClient: The Twitter client if initialized
|
|
100
|
+
"""
|
|
101
|
+
if not self._agent_data:
|
|
102
|
+
self._agent_data = await self._skill_store.get_agent_data(self.agent_id)
|
|
103
|
+
if not self._agent_data:
|
|
104
|
+
raise Exception(f"[{self.agent_id}] Agent data not found")
|
|
105
|
+
if not self._client:
|
|
106
|
+
# Check if we have API keys in config
|
|
107
|
+
if self.use_key:
|
|
108
|
+
self._client = AsyncClient(
|
|
109
|
+
consumer_key=self._config["consumer_key"],
|
|
110
|
+
consumer_secret=self._config["consumer_secret"],
|
|
111
|
+
access_token=self._config["access_token"],
|
|
112
|
+
access_token_secret=self._config["access_token_secret"],
|
|
113
|
+
return_type=dict,
|
|
114
|
+
)
|
|
115
|
+
# refresh userinfo if needed
|
|
116
|
+
if not self._agent_data.twitter_self_key_refreshed_at or (
|
|
117
|
+
self._agent_data.twitter_self_key_refreshed_at
|
|
118
|
+
< datetime.now(tz=timezone.utc) - timedelta(days=1)
|
|
119
|
+
):
|
|
120
|
+
me = await self._client.get_me(
|
|
121
|
+
user_auth=self.use_key,
|
|
122
|
+
user_fields="id,username,name,verified",
|
|
123
|
+
)
|
|
124
|
+
if me and "data" in me and "id" in me["data"]:
|
|
125
|
+
await self._skill_store.set_agent_data(
|
|
126
|
+
self.agent_id,
|
|
127
|
+
{
|
|
128
|
+
"twitter_id": me["data"]["id"],
|
|
129
|
+
"twitter_username": me["data"]["username"],
|
|
130
|
+
"twitter_name": me["data"]["name"],
|
|
131
|
+
"twitter_is_verified": me["data"]["verified"],
|
|
132
|
+
"twitter_self_key_refreshed_at": datetime.now(
|
|
133
|
+
tz=timezone.utc
|
|
134
|
+
),
|
|
135
|
+
},
|
|
136
|
+
)
|
|
137
|
+
self._agent_data = await self._skill_store.get_agent_data(
|
|
138
|
+
self.agent_id
|
|
139
|
+
)
|
|
140
|
+
logger.info(
|
|
141
|
+
f"Twitter self key client initialized. "
|
|
142
|
+
f"Use API key: {self.use_key}, "
|
|
143
|
+
f"User ID: {self.self_id}, "
|
|
144
|
+
f"Username: {self.self_username}, "
|
|
145
|
+
f"Name: {self.self_name}, "
|
|
146
|
+
f"Verified: {self.self_is_verified}"
|
|
147
|
+
)
|
|
148
|
+
return self._client
|
|
149
|
+
# Otherwise try to get OAuth2 tokens from agent data
|
|
150
|
+
if not self._agent_data.twitter_access_token:
|
|
151
|
+
raise Exception(f"[{self.agent_id}] Twitter access token not found")
|
|
152
|
+
if not self._agent_data.twitter_access_token_expires_at:
|
|
153
|
+
raise Exception(
|
|
154
|
+
f"[{self.agent_id}] Twitter access token expiration not found"
|
|
155
|
+
)
|
|
156
|
+
if self._agent_data.twitter_access_token_expires_at <= datetime.now(
|
|
157
|
+
tz=timezone.utc
|
|
158
|
+
):
|
|
159
|
+
raise Exception(f"[{self.agent_id}] Twitter access token has expired")
|
|
160
|
+
self._client = AsyncClient(
|
|
161
|
+
bearer_token=self._agent_data.twitter_access_token,
|
|
162
|
+
return_type=dict,
|
|
163
|
+
)
|
|
164
|
+
return self._client
|
|
165
|
+
if not self.use_key:
|
|
166
|
+
# check if access token has expired
|
|
167
|
+
if self._agent_data.twitter_access_token_expires_at <= datetime.now(
|
|
168
|
+
tz=timezone.utc
|
|
169
|
+
):
|
|
170
|
+
self._agent_data = await self._skill_store.get_agent_data(self.agent_id)
|
|
171
|
+
# check again
|
|
172
|
+
if self._agent_data.twitter_access_token_expires_at <= datetime.now(
|
|
173
|
+
tz=timezone.utc
|
|
174
|
+
):
|
|
175
|
+
raise Exception(
|
|
176
|
+
f"[{self.agent_id}] Twitter access token has expired"
|
|
177
|
+
)
|
|
178
|
+
self._client = AsyncClient(
|
|
179
|
+
bearer_token=self._agent_data.twitter_access_token,
|
|
180
|
+
return_type=dict,
|
|
181
|
+
)
|
|
182
|
+
return self._client
|
|
183
|
+
return self._client
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def self_id(self) -> Optional[str]:
|
|
187
|
+
"""Get the Twitter user ID.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
The Twitter user ID if available, None otherwise
|
|
191
|
+
"""
|
|
192
|
+
if not self._client:
|
|
193
|
+
return None
|
|
194
|
+
if not self._agent_data:
|
|
195
|
+
return None
|
|
196
|
+
return self._agent_data.twitter_id
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def self_username(self) -> Optional[str]:
|
|
200
|
+
"""Get the Twitter username.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
The Twitter username (without @ symbol) if available, None otherwise
|
|
204
|
+
"""
|
|
205
|
+
if not self._client:
|
|
206
|
+
return None
|
|
207
|
+
if not self._agent_data:
|
|
208
|
+
return None
|
|
209
|
+
return self._agent_data.twitter_username
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def self_name(self) -> Optional[str]:
|
|
213
|
+
"""Get the Twitter display name.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
The Twitter display name if available, None otherwise
|
|
217
|
+
"""
|
|
218
|
+
if not self._client:
|
|
219
|
+
return None
|
|
220
|
+
if not self._agent_data:
|
|
221
|
+
return None
|
|
222
|
+
return self._agent_data.twitter_name
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def self_is_verified(self) -> Optional[bool]:
|
|
226
|
+
"""Get the Twitter account verification status.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
The Twitter account verification status if available, None otherwise
|
|
230
|
+
"""
|
|
231
|
+
if not self._client:
|
|
232
|
+
return None
|
|
233
|
+
if not self._agent_data:
|
|
234
|
+
return None
|
|
235
|
+
return self._agent_data.twitter_is_verified
|
|
236
|
+
|
|
237
|
+
def process_tweets_response(self, response: Dict[str, Any]) -> List[Tweet]:
|
|
238
|
+
"""Process Twitter API response and convert it to a list of Tweet objects.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
response: Raw Twitter API response containing tweets data and includes.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
List[Tweet]: List of processed Tweet objects.
|
|
245
|
+
"""
|
|
246
|
+
result = []
|
|
247
|
+
if not response.get("data"):
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
# Create lookup dictionaries from includes
|
|
251
|
+
users_dict = {}
|
|
252
|
+
if response.get("includes") and "users" in response.get("includes"):
|
|
253
|
+
users_dict = {
|
|
254
|
+
user["id"]: TwitterUser(
|
|
255
|
+
id=str(user["id"]),
|
|
256
|
+
name=user["name"],
|
|
257
|
+
username=user["username"],
|
|
258
|
+
description=user["description"],
|
|
259
|
+
public_metrics=user["public_metrics"],
|
|
260
|
+
is_following="following" in user.get("connection_status", []),
|
|
261
|
+
is_follower="followed_by" in user.get("connection_status", []),
|
|
262
|
+
)
|
|
263
|
+
for user in response.get("includes", {}).get("users", [])
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
media_dict = {}
|
|
267
|
+
if response.get("includes") and "media" in response.get("includes"):
|
|
268
|
+
media_dict = {
|
|
269
|
+
media["media_key"]: TwitterMedia(
|
|
270
|
+
media_key=media["media_key"],
|
|
271
|
+
type=media["type"],
|
|
272
|
+
url=media.get("url"),
|
|
273
|
+
)
|
|
274
|
+
for media in response.get("includes", {}).get("media", [])
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
tweets_dict = {}
|
|
278
|
+
if response.get("includes") and "tweets" in response.get("includes"):
|
|
279
|
+
tweets_dict = {
|
|
280
|
+
tweet["id"]: Tweet(
|
|
281
|
+
id=str(tweet["id"]),
|
|
282
|
+
text=tweet["text"],
|
|
283
|
+
author_id=str(tweet["author_id"]),
|
|
284
|
+
created_at=datetime.fromisoformat(
|
|
285
|
+
tweet["created_at"].replace("Z", "+00:00")
|
|
286
|
+
),
|
|
287
|
+
author=users_dict.get(tweet["author_id"]),
|
|
288
|
+
referenced_tweets=None, # Will be populated in second pass
|
|
289
|
+
attachments=None, # Will be populated in second pass
|
|
290
|
+
)
|
|
291
|
+
for tweet in response.get("includes", {}).get("tweets", [])
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
# Process main tweets
|
|
295
|
+
for tweet_data in response["data"]:
|
|
296
|
+
tweet_id = tweet_data["id"]
|
|
297
|
+
author_id = tweet_data["author_id"]
|
|
298
|
+
|
|
299
|
+
# Process attachments if present
|
|
300
|
+
attachments = None
|
|
301
|
+
if (
|
|
302
|
+
"attachments" in tweet_data
|
|
303
|
+
and "media_keys" in tweet_data["attachments"]
|
|
304
|
+
):
|
|
305
|
+
attachments = [
|
|
306
|
+
media_dict[media_key]
|
|
307
|
+
for media_key in tweet_data["attachments"]["media_keys"]
|
|
308
|
+
if media_key in media_dict
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
# Process referenced tweets if present
|
|
312
|
+
referenced_tweets = None
|
|
313
|
+
if "referenced_tweets" in tweet_data:
|
|
314
|
+
referenced_tweets = [
|
|
315
|
+
tweets_dict[ref["id"]]
|
|
316
|
+
for ref in tweet_data["referenced_tweets"]
|
|
317
|
+
if ref["id"] in tweets_dict
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
# Create the Tweet object
|
|
321
|
+
tweet = Tweet(
|
|
322
|
+
id=str(tweet_id),
|
|
323
|
+
text=tweet_data["text"],
|
|
324
|
+
author_id=str(author_id),
|
|
325
|
+
created_at=datetime.fromisoformat(
|
|
326
|
+
tweet_data["created_at"].replace("Z", "+00:00")
|
|
327
|
+
),
|
|
328
|
+
author=users_dict.get(author_id),
|
|
329
|
+
referenced_tweets=referenced_tweets,
|
|
330
|
+
attachments=attachments,
|
|
331
|
+
)
|
|
332
|
+
result.append(tweet)
|
|
333
|
+
|
|
334
|
+
return result
|
|
335
|
+
|
|
336
|
+
async def upload_media(self, agent_id: str, image_url: str) -> List[str]:
|
|
337
|
+
"""Upload media to Twitter and return the media IDs.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
agent_id: The ID of the agent.
|
|
341
|
+
image_url: The URL of the image to upload.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
List[str]: A list of media IDs for the uploaded media.
|
|
345
|
+
|
|
346
|
+
Raises:
|
|
347
|
+
ValueError: If there's an error uploading the media.
|
|
348
|
+
"""
|
|
349
|
+
# Get agent data to access the token
|
|
350
|
+
agent_data = await self._skill_store.get_agent_data(agent_id)
|
|
351
|
+
if not agent_data or not agent_data.twitter_access_token:
|
|
352
|
+
raise ValueError("Only linked X account can post media")
|
|
353
|
+
|
|
354
|
+
media_ids = []
|
|
355
|
+
# Download the image
|
|
356
|
+
async with httpx.AsyncClient() as session:
|
|
357
|
+
response = await session.get(image_url)
|
|
358
|
+
if response.status_code == 200:
|
|
359
|
+
# Create a temporary file to store the image
|
|
360
|
+
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
|
361
|
+
tmp_file.write(response.content)
|
|
362
|
+
tmp_file_path = tmp_file.name
|
|
363
|
+
|
|
364
|
+
# tweepy is outdated, we need to use httpx call new API
|
|
365
|
+
try:
|
|
366
|
+
# Upload the image directly to Twitter using the Media Upload API
|
|
367
|
+
headers = {
|
|
368
|
+
"Authorization": f"Bearer {agent_data.twitter_access_token}"
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
# Upload to Twitter's media/upload endpoint using multipart/form-data
|
|
372
|
+
upload_url = "https://api.twitter.com/2/media/upload"
|
|
373
|
+
|
|
374
|
+
# Get the content type from the response headers or default to image/jpeg
|
|
375
|
+
content_type = response.headers.get("content-type", "image/jpeg")
|
|
376
|
+
|
|
377
|
+
# Create a multipart form with the image file and required parameters
|
|
378
|
+
files = {
|
|
379
|
+
"media": (
|
|
380
|
+
"image",
|
|
381
|
+
open(tmp_file_path, "rb"),
|
|
382
|
+
content_type,
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
# Add required parameters according to new API
|
|
387
|
+
data = {"media_category": "tweet_image", "media_type": content_type}
|
|
388
|
+
|
|
389
|
+
upload_response = await session.post(
|
|
390
|
+
upload_url, headers=headers, files=files, data=data
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if upload_response.status_code == 200:
|
|
394
|
+
media_data = upload_response.json()
|
|
395
|
+
if "data" in media_data and "id" in media_data["data"]:
|
|
396
|
+
media_ids.append(media_data["data"]["id"])
|
|
397
|
+
else:
|
|
398
|
+
raise ValueError(
|
|
399
|
+
f"Unexpected response format from Twitter media upload: {media_data}"
|
|
400
|
+
)
|
|
401
|
+
else:
|
|
402
|
+
raise ValueError(
|
|
403
|
+
f"Failed to upload image to Twitter. Status code: {upload_response.status_code}, Response: {upload_response.text}"
|
|
404
|
+
)
|
|
405
|
+
finally:
|
|
406
|
+
# Clean up the temporary file
|
|
407
|
+
if os.path.exists(tmp_file_path):
|
|
408
|
+
os.unlink(tmp_file_path)
|
|
409
|
+
else:
|
|
410
|
+
raise ValueError(
|
|
411
|
+
f"Failed to download image from URL: {image_url}. Status code: {response.status_code}"
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
return media_ids
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _is_self_key(config: Dict) -> bool:
|
|
418
|
+
return config.get("api_key_provider") == "agent_owner"
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def get_twitter_client(
|
|
422
|
+
agent_id: str, skill_store: SkillStoreABC, config: Dict
|
|
423
|
+
) -> "TwitterClient":
|
|
424
|
+
if _is_self_key(config):
|
|
425
|
+
if agent_id not in _clients_self_key:
|
|
426
|
+
_clients_self_key[agent_id] = TwitterClient(agent_id, skill_store, config)
|
|
427
|
+
return _clients_self_key[agent_id]
|
|
428
|
+
if agent_id not in _clients_linked:
|
|
429
|
+
_clients_linked[agent_id] = TwitterClient(agent_id, skill_store, config)
|
|
430
|
+
return _clients_linked[agent_id]
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
async def unlink_twitter(agent_id: str) -> AgentData:
|
|
434
|
+
logger.info(f"Unlinking Twitter for agent {agent_id}")
|
|
435
|
+
return await AgentData.patch(
|
|
436
|
+
agent_id,
|
|
437
|
+
{
|
|
438
|
+
"twitter_id": None,
|
|
439
|
+
"twitter_username": None,
|
|
440
|
+
"twitter_name": None,
|
|
441
|
+
"twitter_access_token": None,
|
|
442
|
+
"twitter_access_token_expires_at": None,
|
|
443
|
+
"twitter_refresh_token": None,
|
|
444
|
+
},
|
|
445
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# app/config.py
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
|
|
8
|
+
from intentkit.utils.chain import ChainProvider, QuicknodeChainProvider
|
|
9
|
+
from intentkit.utils.logging import setup_logging
|
|
10
|
+
from intentkit.utils.s3 import init_s3
|
|
11
|
+
from intentkit.utils.slack_alert import init_slack
|
|
12
|
+
|
|
13
|
+
# Load environment variables from .env file
|
|
14
|
+
load_dotenv()
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_from_aws(name):
|
|
20
|
+
import botocore.session
|
|
21
|
+
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig
|
|
22
|
+
|
|
23
|
+
client = botocore.session.get_session().create_client("secretsmanager")
|
|
24
|
+
cache_config = SecretCacheConfig()
|
|
25
|
+
cache = SecretCache(config=cache_config, client=client)
|
|
26
|
+
secret = cache.get_secret_string(name)
|
|
27
|
+
return json.loads(secret)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Config:
|
|
31
|
+
def __init__(self):
|
|
32
|
+
# ==== this part can only be load from env
|
|
33
|
+
self.env = os.getenv("ENV", "local")
|
|
34
|
+
self.release = os.getenv("RELEASE", "local")
|
|
35
|
+
secret_name = os.getenv("AWS_SECRET_NAME")
|
|
36
|
+
db_secret_name = os.getenv("AWS_DB_SECRET_NAME")
|
|
37
|
+
# ==== load from aws secrets manager
|
|
38
|
+
if secret_name:
|
|
39
|
+
self.secrets = load_from_aws(secret_name)
|
|
40
|
+
else:
|
|
41
|
+
self.secrets = {}
|
|
42
|
+
if db_secret_name:
|
|
43
|
+
self.db = load_from_aws(db_secret_name)
|
|
44
|
+
# format the db config
|
|
45
|
+
self.db["port"] = str(self.db["port"])
|
|
46
|
+
# only keep the necessary fields
|
|
47
|
+
self.db = {
|
|
48
|
+
k: v
|
|
49
|
+
for k, v in self.db.items()
|
|
50
|
+
if k in ["username", "password", "host", "dbname", "port"]
|
|
51
|
+
}
|
|
52
|
+
else:
|
|
53
|
+
self.db = {
|
|
54
|
+
"username": os.getenv("DB_USERNAME"),
|
|
55
|
+
"password": os.getenv("DB_PASSWORD"),
|
|
56
|
+
"host": os.getenv("DB_HOST"),
|
|
57
|
+
"port": os.getenv("DB_PORT"),
|
|
58
|
+
"dbname": os.getenv("DB_NAME"),
|
|
59
|
+
}
|
|
60
|
+
# ==== this part can be load from env or aws secrets manager
|
|
61
|
+
self.db["auto_migrate"] = self.load("DB_AUTO_MIGRATE", "true") == "true"
|
|
62
|
+
self.debug = self.load("DEBUG") == "true"
|
|
63
|
+
self.debug_checkpoint = (
|
|
64
|
+
self.load("DEBUG_CHECKPOINT", "false") == "true"
|
|
65
|
+
) # log with checkpoint
|
|
66
|
+
# Redis
|
|
67
|
+
self.redis_host = self.load("REDIS_HOST")
|
|
68
|
+
self.redis_port = int(self.load("REDIS_PORT", "6379"))
|
|
69
|
+
# AWS
|
|
70
|
+
self.aws_s3_bucket = self.load("AWS_S3_BUCKET")
|
|
71
|
+
self.aws_s3_cdn_url = self.load("AWS_S3_CDN_URL")
|
|
72
|
+
# Internal API
|
|
73
|
+
self.internal_base_url = self.load("INTERNAL_BASE_URL", "http://intent-api")
|
|
74
|
+
self.admin_auth_enabled = self.load("ADMIN_AUTH_ENABLED", "false") == "true"
|
|
75
|
+
self.admin_jwt_secret = self.load("ADMIN_JWT_SECRET")
|
|
76
|
+
self.debug_auth_enabled = self.load("DEBUG_AUTH_ENABLED", "false") == "true"
|
|
77
|
+
self.debug_username = self.load("DEBUG_USERNAME")
|
|
78
|
+
self.debug_password = self.load("DEBUG_PASSWORD")
|
|
79
|
+
self.admin_llm_skill_control = (
|
|
80
|
+
self.load("ADMIN_LLM_SKILL_CONTROL", "false") == "true"
|
|
81
|
+
)
|
|
82
|
+
# Payment
|
|
83
|
+
self.payment_enabled = self.load("PAYMENT_ENABLED", "false") == "true"
|
|
84
|
+
# Open API for agent
|
|
85
|
+
self.open_api_base_url = self.load("OPEN_API_BASE_URL", "http://localhost:8000")
|
|
86
|
+
# CDP
|
|
87
|
+
self.cdp_api_key_name = self.load("CDP_API_KEY_NAME")
|
|
88
|
+
self.cdp_api_key_private_key = self.load("CDP_API_KEY_PRIVATE_KEY")
|
|
89
|
+
# LLM providers
|
|
90
|
+
self.openai_api_key = self.load("OPENAI_API_KEY")
|
|
91
|
+
self.deepseek_api_key = self.load("DEEPSEEK_API_KEY")
|
|
92
|
+
self.xai_api_key = self.load("XAI_API_KEY")
|
|
93
|
+
self.eternal_api_key = self.load("ETERNAL_API_KEY")
|
|
94
|
+
self.reigent_api_key = self.load("REIGENT_API_KEY")
|
|
95
|
+
self.venice_api_key = self.load("VENICE_API_KEY")
|
|
96
|
+
self.system_prompt = self.load("SYSTEM_PROMPT")
|
|
97
|
+
self.input_token_limit = int(self.load("INPUT_TOKEN_LIMIT", "60000"))
|
|
98
|
+
# Telegram server settings
|
|
99
|
+
self.tg_base_url = self.load("TG_BASE_URL")
|
|
100
|
+
self.tg_server_host = self.load("TG_SERVER_HOST", "127.0.0.1")
|
|
101
|
+
self.tg_server_port = self.load("TG_SERVER_PORT", "8081")
|
|
102
|
+
self.tg_new_agent_poll_interval = self.load("TG_NEW_AGENT_POLL_INTERVAL", "60")
|
|
103
|
+
# Twitter
|
|
104
|
+
self.twitter_oauth2_client_id = self.load("TWITTER_OAUTH2_CLIENT_ID")
|
|
105
|
+
self.twitter_oauth2_client_secret = self.load("TWITTER_OAUTH2_CLIENT_SECRET")
|
|
106
|
+
self.twitter_oauth2_redirect_uri = self.load("TWITTER_OAUTH2_REDIRECT_URI")
|
|
107
|
+
self.twitter_entrypoint_interval = int(
|
|
108
|
+
self.load("TWITTER_ENTRYPOINT_INTERVAL", "5")
|
|
109
|
+
) # in minutes
|
|
110
|
+
# Slack Alert
|
|
111
|
+
self.slack_alert_token = self.load("SLACK_ALERT_TOKEN")
|
|
112
|
+
self.slack_alert_channel = self.load("SLACK_ALERT_CHANNEL")
|
|
113
|
+
# Skills - Platform Hosted Keys
|
|
114
|
+
self.acolyt_api_key = self.load("ACOLYT_API_KEY")
|
|
115
|
+
self.allora_api_key = self.load("ALLORA_API_KEY")
|
|
116
|
+
self.elfa_api_key = self.load("ELFA_API_KEY")
|
|
117
|
+
self.heurist_api_key = self.load("HEURIST_API_KEY")
|
|
118
|
+
self.enso_api_token = self.load("ENSO_API_TOKEN")
|
|
119
|
+
self.dapplooker_api_key = self.load("DAPPLOOKER_API_KEY")
|
|
120
|
+
self.moralis_api_key = self.load("MORALIS_API_KEY")
|
|
121
|
+
self.tavily_api_key = self.load("TAVILY_API_KEY")
|
|
122
|
+
self.cookiefun_api_key = self.load("COOKIEFUN_API_KEY")
|
|
123
|
+
# Sentry
|
|
124
|
+
self.sentry_dsn = self.load("SENTRY_DSN")
|
|
125
|
+
self.sentry_sample_rate = float(self.load("SENTRY_SAMPLE_RATE", "0.1"))
|
|
126
|
+
self.sentry_traces_sample_rate = float(
|
|
127
|
+
self.load("SENTRY_TRACES_SAMPLE_RATE", "0.01")
|
|
128
|
+
)
|
|
129
|
+
self.sentry_profiles_sample_rate = float(
|
|
130
|
+
self.load("SENTRY_PROFILES_SAMPLE_RATE", "0.01")
|
|
131
|
+
)
|
|
132
|
+
# RPC Providers
|
|
133
|
+
self.quicknode_api_key = self.load("QUICKNODE_API_KEY")
|
|
134
|
+
if self.quicknode_api_key:
|
|
135
|
+
self.chain_provider: ChainProvider = QuicknodeChainProvider(
|
|
136
|
+
self.quicknode_api_key
|
|
137
|
+
)
|
|
138
|
+
if hasattr(self, "chain_provider"):
|
|
139
|
+
self.chain_provider.init_chain_configs()
|
|
140
|
+
self.rpc_networks = self.load(
|
|
141
|
+
"RPC_NETWORKS", "base-mainnet,base-sepolia,ethereum-sepolia,solana-mainnet"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Nation
|
|
145
|
+
self.nation_api_key = self.load("NATION_API_KEY")
|
|
146
|
+
self.nation_api_url = self.load("NATION_API_URL", "")
|
|
147
|
+
|
|
148
|
+
# ===== config loaded
|
|
149
|
+
# Now we know the env, set up logging
|
|
150
|
+
setup_logging(self.env, self.debug)
|
|
151
|
+
logger.info("config loaded")
|
|
152
|
+
# If the slack alert token exists, init it
|
|
153
|
+
if self.slack_alert_token and self.slack_alert_channel:
|
|
154
|
+
init_slack(self.slack_alert_token, self.slack_alert_channel)
|
|
155
|
+
# If the AWS S3 bucket and CDN URL exist, init it
|
|
156
|
+
if self.aws_s3_bucket and self.aws_s3_cdn_url:
|
|
157
|
+
init_s3(self.aws_s3_bucket, self.aws_s3_cdn_url, self.env)
|
|
158
|
+
|
|
159
|
+
def load(self, key, default=None):
|
|
160
|
+
"""Load a secret from the secrets map or env"""
|
|
161
|
+
return self.secrets.get(key, os.getenv(key, default))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
config: Config = Config()
|
|
File without changes
|