From 551a3448ff1cad061e6c9faee5216529427c7d92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 22:03:46 +0000 Subject: [PATCH 1/4] Initial plan From eb1f9d2507819dd8dd59613f551ec357a4cb88f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 22:09:22 +0000 Subject: [PATCH 2/4] Phase 1-3: Implement autonomous personality-driven tweet agent with continuous posting Co-authored-by: mostlyfutures <199047654+mostlyfutures@users.noreply.github.com> --- .env_example | 8 ++ src/agents/tweet_agent.py | 163 +++++++++++++++++++++++++++++++++----- src/config.py | 12 +++ 3 files changed, 163 insertions(+), 20 deletions(-) diff --git a/.env_example b/.env_example index 244499efc..603f474f7 100644 --- a/.env_example +++ b/.env_example @@ -55,6 +55,14 @@ TWITTER_USERNAME=your_twitter_username_here TWITTER_EMAIL=your_twitter_email_here TWITTER_PASSWORD=your_twitter_password_here +# Twitter API v2 credentials (for automated posting with tweet_agent.py) +# Get these from https://developer.twitter.com/en/portal/dashboard +TWITTER_API_KEY=your_twitter_api_key_here +TWITTER_API_SECRET=your_twitter_api_secret_here +TWITTER_ACCESS_TOKEN=your_twitter_access_token_here +TWITTER_ACCESS_TOKEN_SECRET=your_twitter_access_token_secret_here +TWITTER_BEARER_TOKEN=your_twitter_bearer_token_here + # google keys GOOGLE_APPLICATION_CREDENTIALS=your_google_credentials_path diff --git a/src/agents/tweet_agent.py b/src/agents/tweet_agent.py index ec1e19af2..5e7ecc995 100644 --- a/src/agents/tweet_agent.py +++ b/src/agents/tweet_agent.py @@ -55,14 +55,19 @@ {text} Manifest: -- Keep it casual and concise -- Focus on key insights and facts -- no emojis -- always be kind -- No hashtags unless absolutely necessary -- Maximum 280 characters per tweet -- no capitalization -- don't number the tweets +- casual, conversational tone with technical depth +- lowercase writing style (no capitalization except for acronyms like AI, API, USD) +- use emojis strategically (šŸš€ šŸŒ™ šŸ’° šŸ¤– ⚔ šŸŽÆ) but not excessively +- focus on ai agents, trading, crypto, and futurism +- include tech jargon but explain complex concepts simply +- short declarative sentences that pack insights +- emphasize experimental/educational nature of ideas +- always be kind and supportive to the community +- use "we" and "you" to be inclusive +- mention discord community engagement when relevant +- no numbered lists in tweets +- hashtags only when natural (lowercase: #ai #trading #crypto) +- maximum 280 characters per tweet - separate tweets with blank lines EACH TWEET MUST BE A COMPLETE TAKE AND BE INTERESTING @@ -123,6 +128,33 @@ def __init__(self): else: self.deepseek_client = None + # Initialize Twitter API client (optional - only if credentials are provided) + self.twitter_client = None + try: + twitter_api_key = os.getenv("TWITTER_API_KEY") or config.TWITTER_API_KEY + twitter_api_secret = os.getenv("TWITTER_API_SECRET") or config.TWITTER_API_SECRET + twitter_access_token = os.getenv("TWITTER_ACCESS_TOKEN") or config.TWITTER_ACCESS_TOKEN + twitter_access_token_secret = os.getenv("TWITTER_ACCESS_TOKEN_SECRET") or config.TWITTER_ACCESS_TOKEN_SECRET + + if twitter_api_key and twitter_api_secret and twitter_access_token and twitter_access_token_secret: + import tweepy + # Twitter API v2 authentication + self.twitter_client = tweepy.Client( + consumer_key=twitter_api_key, + consumer_secret=twitter_api_secret, + access_token=twitter_access_token, + access_token_secret=twitter_access_token_secret + ) + print("āœ… Twitter API client initialized successfully") + else: + print("ā„¹ļø Twitter API credentials not found - tweets will only be saved to file") + except ImportError: + print("āš ļø tweepy not installed - run: pip install tweepy") + self.twitter_client = None + except Exception as e: + print(f"āš ļø Error initializing Twitter client: {str(e)}") + self.twitter_client = None + # Create tweets directory if it doesn't exist self.tweets_dir = Path("/Users/md/Dropbox/dev/github/moon-dev-ai-agents-for-trading/src/data/tweets") self.tweets_dir.mkdir(parents=True, exist_ok=True) @@ -253,17 +285,108 @@ def generate_tweets(self, text=None): print(f"āŒ Error generating tweets: {str(e)}") traceback.print_exc() return None + + def post_tweet(self, tweet_text): + """ + Post a tweet to Twitter using the API + + Args: + tweet_text (str): The text content to tweet (max 280 characters) + + Returns: + bool: True if posted successfully, False otherwise + """ + if not self.twitter_client: + print("āš ļø Twitter client not initialized - skipping posting") + return False + + try: + # Validate tweet length + if len(tweet_text) > 280: + print(f"āš ļø Tweet too long ({len(tweet_text)} chars), truncating to 280...") + tweet_text = tweet_text[:277] + "..." + + # Post the tweet + response = self.twitter_client.create_tweet(text=tweet_text) + + if response and response.data: + tweet_id = response.data.get('id', 'unknown') + print(f"āœ… Tweet posted successfully! ID: {tweet_id}") + return True + else: + print("āš ļø Tweet posted but no response data received") + return False + + except Exception as e: + print(f"āŒ Error posting tweet: {str(e)}") + # Don't crash the agent, just log the error and continue + return False if __name__ == "__main__": - agent = TweetAgent() - - # Example usage with direct text - test_text = """Bitcoin showing strong momentum with increasing volume. - Price action suggests accumulation phase might be complete. - Key resistance at $69,000 with support holding at $65,000.""" - - # If USE_TEXT_FILE is True, it will use the file instead of test_text - tweets = agent.generate_tweets(test_text) - - if tweets: - print(f"\nTweets have been saved to: {agent.output_file}") + try: + agent = TweetAgent() + + # Example usage with direct text + test_text = """Bitcoin showing strong momentum with increasing volume. + Price action suggests accumulation phase might be complete. + Key resistance at $69,000 with support holding at $65,000.""" + + print(f"\nšŸŒ™ Moon Dev's Tweet Agent starting (interval: {config.TWEET_INTERVAL_SECONDS}s)...") + + while True: + try: + print(f"\n{'='*60}") + print(f"šŸ”„ Starting new tweet generation cycle at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"{'='*60}") + + # If USE_TEXT_FILE is True, it will use the file instead of test_text + tweets = agent.generate_tweets(test_text) + + if tweets: + print(f"\nāœ… Generated {len(tweets)} tweets successfully") + print(f"šŸ“ Tweets saved to: {agent.output_file}") + + # Post tweets to Twitter if client is initialized + if agent.twitter_client: + print(f"\n🐦 Posting {len(tweets)} tweets to Twitter...") + posted_count = 0 + + for idx, tweet in enumerate(tweets, 1): + print(f"\nšŸ“¤ Posting tweet {idx}/{len(tweets)}:") + print(f" {tweet[:100]}{'...' if len(tweet) > 100 else ''}") + + success = agent.post_tweet(tweet) + if success: + posted_count += 1 + + # Delay between posts to avoid rate limits + if idx < len(tweets): + time.sleep(config.TWEET_POST_DELAY_SECONDS) + + print(f"\nāœ… Posted {posted_count}/{len(tweets)} tweets successfully") + else: + print("\nā„¹ļø Twitter posting disabled (no API credentials)") + + # Calculate next run time + next_run = datetime.now() + pd.Timedelta(seconds=config.TWEET_INTERVAL_SECONDS) + print(f"\n😓 Generation complete. Sleeping for {config.TWEET_INTERVAL_SECONDS} seconds...") + print(f"ā° Next run at: {next_run.strftime('%Y-%m-%d %H:%M:%S')}") + print(f"{'='*60}\n") + + time.sleep(config.TWEET_INTERVAL_SECONDS) + + except KeyboardInterrupt: + raise + except Exception as e: + print(f"\nāŒ Error in tweet generation cycle: {str(e)}") + traceback.print_exc() + print(f"\nā³ Waiting 60 seconds before retrying...") + time.sleep(60) # Wait before retrying on error + + except KeyboardInterrupt: + print("\n\nšŸ‘‹ Moon Dev's Tweet Agent shutting down gracefully...") + print("šŸŒ™ Thanks for using the Tweet Agent! šŸš€") + except Exception as e: + print(f"\nāŒ Fatal error: {str(e)}") + traceback.print_exc() + sys.exit(1) diff --git a/src/config.py b/src/config.py index dffbbf718..3233f5f71 100644 --- a/src/config.py +++ b/src/config.py @@ -133,3 +133,15 @@ def get_active_tokens(): DO_NOT_TRADE_LIST = ['777'] CLOSED_POSITIONS_TXT = '777' minimum_trades_in_last_hour = 777 + +# Tweet Agent Settings 🐦 +TWEET_INTERVAL_SECONDS = 900 # 15 minutes (900 seconds) between tweet generation cycles +TWEET_POST_DELAY_SECONDS = 10 # Delay between posting individual tweets to avoid rate limits + +# Twitter API Credentials (v2 API with tweepy) +# Get these from https://developer.twitter.com/en/portal/dashboard +TWITTER_API_KEY = "" # Your Twitter API Key +TWITTER_API_SECRET = "" # Your Twitter API Secret +TWITTER_ACCESS_TOKEN = "" # Your Twitter Access Token +TWITTER_ACCESS_TOKEN_SECRET = "" # Your Twitter Access Token Secret +TWITTER_BEARER_TOKEN = "" # Your Twitter Bearer Token (optional, for read-only operations) From cefbe57844a1c1902cab054ee6b6d58a103a9be7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 22:12:48 +0000 Subject: [PATCH 3/4] Fix hardcoded file paths to use relative PROJECT_ROOT paths Co-authored-by: mostlyfutures <199047654+mostlyfutures@users.noreply.github.com> --- src/agents/tweet_agent.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/agents/tweet_agent.py b/src/agents/tweet_agent.py index 5e7ecc995..220409a0c 100644 --- a/src/agents/tweet_agent.py +++ b/src/agents/tweet_agent.py @@ -17,9 +17,9 @@ # Text Processing Settings MAX_CHUNK_SIZE = 10000 # Maximum characters per chunk TWEETS_PER_CHUNK = 3 # Number of tweets to generate per chunk -USE_TEXT_FILE = True # Whether to use og_tweet_text.txt by default -# if the above is true, then the below is the file to use -OG_TWEET_FILE = "/Users/md/Dropbox/dev/github/moon-dev-ai-agents-for-trading/src/data/tweets/og_tweet_text.txt" +USE_TEXT_FILE = False # Whether to use og_tweet_text.txt by default (set to False for standalone testing) +# if the above is true, then the below is the file to use (relative to project root) +# OG_TWEET_FILE will be constructed at runtime to avoid hardcoded paths import os import pandas as pd @@ -155,8 +155,8 @@ def __init__(self): print(f"āš ļø Error initializing Twitter client: {str(e)}") self.twitter_client = None - # Create tweets directory if it doesn't exist - self.tweets_dir = Path("/Users/md/Dropbox/dev/github/moon-dev-ai-agents-for-trading/src/data/tweets") + # Create tweets directory if it doesn't exist (relative to project root) + self.tweets_dir = PROJECT_ROOT / "src" / "data" / "tweets" self.tweets_dir.mkdir(parents=True, exist_ok=True) # Generate output filename with timestamp @@ -172,7 +172,9 @@ def _get_input_text(self, text=None): """Get input text from either file or direct input""" if USE_TEXT_FILE: try: - with open(OG_TWEET_FILE, 'r') as f: + # Construct path relative to project root + og_tweet_file = PROJECT_ROOT / "src" / "data" / "tweets" / "og_tweet_text.txt" + with open(og_tweet_file, 'r') as f: return f.read() except Exception as e: print(f"āŒ Error reading text file: {str(e)}") From 72f8ee3fdc7aedd19f1492086117661c504ec83a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 22:15:21 +0000 Subject: [PATCH 4/4] Add comprehensive documentation for autonomous tweet agent Co-authored-by: mostlyfutures <199047654+mostlyfutures@users.noreply.github.com> --- docs/tweet_agent.md | 276 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 254 insertions(+), 22 deletions(-) diff --git a/docs/tweet_agent.md b/docs/tweet_agent.md index 2b0eab675..53a65b8a1 100644 --- a/docs/tweet_agent.md +++ b/docs/tweet_agent.md @@ -1,37 +1,269 @@ -# Tweet Agent +# Tweet Agent 🐦 -Generates trading-related tweets automatically. +An autonomous AI agent that continuously generates and posts tweets in a customized personality-driven style. ## What It Does -- Creates market commentary tweets -- Generates token analysis threads -- Writes educational content -- Schedules tweet timing +- šŸ¤– Generates tweets using AI (DeepSeek, Claude, or GPT) +- šŸ”„ Runs continuously with configurable intervals +- 🐦 Auto-posts to Twitter via API v2 (optional) +- šŸ’¾ Saves all generated tweets to file +- šŸŽÆ Uses customizable personality manifest +- ⚔ Handles errors gracefully and continues running -## Usage +## Features + +### Personality-Driven Writing +The agent uses a detailed personality manifest to ensure consistent voice: +- Casual, lowercase style (Moon Dev's voice) +- Strategic emoji use (šŸš€ šŸŒ™ šŸ’° šŸ¤– ⚔ šŸŽÆ) +- Tech jargon with simple explanations +- Community-focused language +- Focus on AI agents, trading, crypto, futurism + +### Autonomous Operation +- Runs in continuous loop (default: 15 minutes per cycle) +- Automatic tweet generation and posting +- Keyboard interrupt support (Ctrl+C for graceful shutdown) +- Error recovery without crashing + +### Safe Mode +Works without Twitter API credentials - just saves tweets to file for review before manual posting. + +## Quick Start + +### 1. Basic Usage (No Twitter API - Safe Mode) +```bash +python src/agents/tweet_agent.py +``` + +This will: +- Generate tweets every 15 minutes +- Save to `src/data/tweets/generated_tweets_TIMESTAMP.txt` +- NOT post to Twitter (requires API credentials) + +### 2. With Twitter Auto-Posting + +**Step 1: Get Twitter API Credentials** +1. Go to https://developer.twitter.com/en/portal/dashboard +2. Create a new app (or use existing) +3. Generate API keys and access tokens +4. Make sure you have "Read and Write" permissions + +**Step 2: Add Credentials to `.env`** +```bash +# Twitter API v2 credentials +TWITTER_API_KEY=your_api_key_here +TWITTER_API_SECRET=your_api_secret_here +TWITTER_ACCESS_TOKEN=your_access_token_here +TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret_here +``` + +**Step 3: Run the Agent** ```bash python src/agents/tweet_agent.py ``` -## Tweet Types -- Market updates -- Token discoveries -- Technical analysis -- Risk warnings -- Educational threads +The agent will now automatically post generated tweets to your Twitter account! ## Configuration + +All settings are in `src/config.py`: + +```python +# Tweet Agent Settings +TWEET_INTERVAL_SECONDS = 900 # 15 minutes between cycles +TWEET_POST_DELAY_SECONDS = 10 # Delay between posting individual tweets + +# Twitter API Credentials (or use .env) +TWITTER_API_KEY = "" +TWITTER_API_SECRET = "" +TWITTER_ACCESS_TOKEN = "" +TWITTER_ACCESS_TOKEN_SECRET = "" +``` + +### Customizing the Interval + +To change how often tweets are generated, edit `src/config.py`: + +```python +TWEET_INTERVAL_SECONDS = 1800 # 30 minutes +# TWEET_INTERVAL_SECONDS = 3600 # 1 hour +# TWEET_INTERVAL_SECONDS = 14400 # 4 hours +``` + +## Customizing the Personality + +The personality manifest is in `src/agents/tweet_agent.py` (lines 49-68): + +```python +TWEET_PROMPT = """... + +Manifest: +- casual, conversational tone with technical depth +- lowercase writing style (no capitalization except for acronyms) +- use emojis strategically (šŸš€ šŸŒ™ šŸ’° šŸ¤– ⚔ šŸŽÆ) +- focus on ai agents, trading, crypto, and futurism +... +``` + +Edit this manifest to change the agent's writing style! + +## Advanced Usage + +### Custom Text Input + +By default, the agent uses example text. To provide your own content: + +**Option 1: Direct text (edit the script)** +```python +test_text = """Your custom content here""" +``` + +**Option 2: Text file** +1. Set `USE_TEXT_FILE = True` in `tweet_agent.py` +2. Create file: `src/data/tweets/og_tweet_text.txt` +3. Add your source content to that file + +The agent will chunk long text and generate tweets from each chunk. + +### Tweet Generation Settings + +In `src/agents/tweet_agent.py`: + ```python -TWEET_FREQUENCY = 'hourly' # or 'daily', 'manual' -TWEET_MAX_LENGTH = 280 -TWEET_INCLUDE_CHARTS = True -TWEET_AUTO_POST = False # Set True to auto-post +MAX_CHUNK_SIZE = 10000 # Maximum characters per chunk +TWEETS_PER_CHUNK = 3 # Number of tweets to generate per chunk ``` -## Safety -- No financial advice disclaimers -- No token shilling -- Fact-based content only +### AI Model Selection + +```python +# In tweet_agent.py (line 14) +MODEL_OVERRIDE = "deepseek-chat" # or "0" to use config.AI_MODEL +``` + +Available models: +- `"deepseek-chat"` - Fast & efficient (default) +- `"deepseek-reasoner"` - DeepSeek's reasoning model +- `"claude-3-haiku-20240307"` - Fast Claude +- `"claude-3-sonnet-20240229"` - Balanced Claude +- `"0"` - Use `config.AI_MODEL` setting ## Output -`src/data/tweets/[date]/generated_tweets.txt` \ No newline at end of file + +### File Output +All tweets are saved to timestamped files: +``` +src/data/tweets/generated_tweets_20250115_143022.txt +``` + +Format: +``` +first tweet content here + +second tweet content here + +third tweet content here +``` + +### Console Output +The agent provides detailed logging: +``` +šŸ”„ Starting new tweet generation cycle at 2025-01-15 14:30:22 +šŸ“Š Text Analysis: +Total characters: 256 +... +āœ… Generated 3 tweets successfully +šŸ“¤ Posting tweet 1/3: +āœ… Tweet posted successfully! ID: 1234567890 +😓 Next run at: 2025-01-15 14:45:22 +``` + +## Safety Features + +### Tweet Validation +- āœ… Automatic length validation (280 char limit) +- āœ… Truncation with ellipsis if too long +- āœ… Character count displayed before posting + +### Error Handling +- āœ… API errors don't crash the agent +- āœ… Rate limit protection via delays +- āœ… Network failures logged and retried +- āœ… Graceful shutdown on Ctrl+C + +### Rate Limit Protection +- Default 10-second delay between posts +- Configurable via `TWEET_POST_DELAY_SECONDS` +- Respects Twitter's API rate limits + +## Troubleshooting + +### "Twitter API credentials not found" +- Add credentials to `.env` file +- Agent will run in safe mode (file-only output) + +### "tweepy not installed" +- Run: `pip install tweepy` +- Or: `pip install -r requirements.txt` + +### "DeepSeek client not initialized" +- Add `DEEPSEEK_KEY` to `.env` +- Or set `MODEL_OVERRIDE = "0"` and use Claude/GPT + +### Tweets not posting +1. Check Twitter API credentials are correct +2. Verify app has "Read and Write" permissions +3. Check console for error messages +4. Ensure tweets are under 280 characters + +## Best Practices + +1. **Start in Safe Mode**: Run without Twitter API first to review generated tweets +2. **Test with Short Intervals**: Use `TWEET_INTERVAL_SECONDS = 60` for testing +3. **Monitor the Output**: Watch the first few cycles to ensure quality +4. **Customize the Personality**: Edit the manifest to match your voice +5. **Use Good Source Content**: Better input = better tweets + +## Example Workflow + +```bash +# 1. Test tweet generation (no posting) +python src/agents/tweet_agent.py +# Review output in src/data/tweets/ + +# 2. Add Twitter credentials to .env +# 3. Test with short interval +# Edit config.py: TWEET_INTERVAL_SECONDS = 120 + +# 4. Run with auto-posting +python src/agents/tweet_agent.py + +# 5. Monitor first few cycles +# 6. Increase interval for production +# Edit config.py: TWEET_INTERVAL_SECONDS = 3600 + +# 7. Run long-term +nohup python src/agents/tweet_agent.py & +``` + +## Integration with Other Agents + +The tweet agent can work with other agents in the ecosystem: + +- **Chat Agent**: Generate tweets from stream insights +- **Research Agent**: Tweet about new trading strategies +- **Sentiment Agent**: Tweet market sentiment analysis +- **RBI Agent**: Share backtest results + +## Disclaimer + +āš ļø **Use Responsibly** +- Review generated content before auto-posting +- Ensure compliance with Twitter's Terms of Service +- Don't spam or post misleading information +- This is an experimental tool for educational purposes + +--- + +*Built with love by Moon Dev šŸŒ™ - Part of the Moon Dev AI Agents ecosystem* \ No newline at end of file