Twitter Plugin Examples

This document provides practical examples of using the @elizaos/plugin-twitter package in various scenarios.

Basic Bot Setup

Simple Posting Bot

Create a basic Twitter bot that posts autonomously:

import { AgentRuntime } from '@elizaos/core';
import { twitterPlugin } from '@elizaos/plugin-twitter';
import { bootstrapPlugin } from '@elizaos/plugin-bootstrap';

const character = {
  name: "SimpleTwitterBot",
  description: "A simple Twitter bot that posts updates",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  postExamples: [
    "Just thinking about the future of technology...",
    "Building something new today! 🚀",
    "The best code is no code, but sometimes you need to write some.",
    "Learning something new every day keeps the mind sharp."
  ],
  settings: {
    TWITTER_API_KEY: process.env.TWITTER_API_KEY,
    TWITTER_API_SECRET_KEY: process.env.TWITTER_API_SECRET_KEY,
    TWITTER_ACCESS_TOKEN: process.env.TWITTER_ACCESS_TOKEN,
    TWITTER_ACCESS_TOKEN_SECRET: process.env.TWITTER_ACCESS_TOKEN_SECRET,
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_IMMEDIATELY: "true",
    TWITTER_POST_INTERVAL_MIN: "120",
    TWITTER_POST_INTERVAL_MAX: "240"
  }
};

// Create and start the runtime
const runtime = new AgentRuntime({ character });
await runtime.start();

console.log('Twitter bot is running and will post every 2-4 hours!');

Content Creator Bot

Bot focused on creating engaging content:

const contentCreatorBot = {
  name: "ContentCreator",
  description: "Creates engaging Twitter content",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  postExamples: [
    "🧵 Thread: Let's talk about why decentralization matters...",
    "Hot take: The future of AI isn't about replacing humans, it's about augmentation",
    "Day 30 of building in public: Today I learned...",
    "Unpopular opinion: Simplicity > Complexity in system design",
    "What's your biggest challenge in tech right now? Let's discuss 👇"
  ],
  topics: [
    "artificial intelligence",
    "web3 and blockchain",
    "software engineering",
    "startups and entrepreneurship",
    "future of technology"
  ],
  style: {
    tone: "thought-provoking but approachable",
    format: "mix of threads, questions, and insights",
    emoji: "use sparingly for emphasis"
  },
  settings: {
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_INTERVAL_MIN: "90",
    TWITTER_POST_INTERVAL_MAX: "180",
    TWITTER_POST_INTERVAL_VARIANCE: "0.3",
    TWITTER_MAX_TWEET_LENGTH: "280"  // Keep it concise
  }
};

Thread Poster

Bot that creates detailed threads:

const threadPosterBot = {
  name: "ThreadMaster",
  description: "Creates informative Twitter threads",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  postExamples: [
    "🧵 A thread on system design principles:\n\n1/ Start with the problem, not the solution",
    "🧵 How to build a successful side project:\n\n1/ Pick something you'll use yourself",
    "🧵 Lessons learned from 10 years in tech:\n\n1/ Technology changes, principles remain"
  ],
  settings: {
    TWITTER_POST_ENABLE: "true",
    TWITTER_MAX_TWEET_LENGTH: "4000",  // Support for longer threads
    // Custom action for thread creation
    customActions: ["CREATE_THREAD"]
  }
};

// Custom thread creation action
const createThreadAction: Action = {
  name: "CREATE_THREAD",
  description: "Creates a Twitter thread",
  
  handler: async (runtime, message, state, options, callback) => {
    const topic = options.topic || "technology trends";
    
    // Generate thread content
    const threadContent = await runtime.generateText({
      messages: [{
        role: "system",
        content: `Create a Twitter thread about ${topic}. Format as numbered tweets (1/, 2/, etc). Make it informative and engaging.`
      }],
      maxTokens: 1000
    });
    
    // Split into individual tweets
    const tweets = threadContent.text.split(/\d+\//).filter(t => t.trim());
    
    // Post as thread
    let previousTweetId = null;
    for (const tweet of tweets) {
      const response = await runtime.getService('twitter').client.tweet(
        tweet.trim(),
        previousTweetId ? { reply: { in_reply_to_tweet_id: previousTweetId } } : {}
      );
      previousTweetId = response.id;
    }
    
    await callback({
      text: `Thread posted! First tweet: ${tweets[0].substring(0, 50)}...`
    });
    
    return true;
  }
};

Interaction Bots

Reply Bot

Bot that responds to mentions and replies:

const replyBot = {
  name: "ReplyBot",
  description: "Responds to mentions and conversations",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_POST_ENABLE: "false",  // Don't post autonomously
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_AUTO_RESPOND_MENTIONS: "true",
    TWITTER_AUTO_RESPOND_REPLIES: "true",
    TWITTER_POLL_INTERVAL: "60",  // Check every minute
    TWITTER_INTERACTION_INTERVAL_MIN: "5",
    TWITTER_INTERACTION_INTERVAL_MAX: "15"
  },
  responseExamples: [
    {
      input: "What do you think about AI?",
      output: "AI is a tool that amplifies human capability. The key is ensuring it serves humanity's best interests."
    },
    {
      input: "Can you help me with coding?",
      output: "I'd be happy to help! What specific coding challenge are you working on?"
    }
  ]
};

Mention Handler

Bot that processes specific mentions:

const mentionHandler = {
  name: "MentionBot",
  description: "Handles mentions with specific commands",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_AUTO_RESPOND_MENTIONS: "true"
  }
};

// Custom mention handler
const handleMentionAction: Action = {
  name: "HANDLE_MENTION",
  description: "Process mention commands",
  
  handler: async (runtime, message, state, options, callback) => {
    const text = message.content.text.toLowerCase();
    const twitterService = runtime.getService('twitter');
    
    // Command: @bot summarize [url]
    if (text.includes('summarize')) {
      const urlMatch = text.match(/https?:\/\/[^\s]+/);
      if (urlMatch) {
        const summary = await summarizeUrl(urlMatch[0]);
        await callback({
          text: `Summary: ${summary}`,
          replyTo: message.id
        });
      }
    }
    
    // Command: @bot remind me [message] in [time]
    else if (text.includes('remind me')) {
      const reminderMatch = text.match(/remind me (.+) in (\d+) (minutes?|hours?)/);
      if (reminderMatch) {
        const [, message, amount, unit] = reminderMatch;
        const delay = unit.startsWith('hour') ? amount * 60 * 60 * 1000 : amount * 60 * 1000;
        
        setTimeout(async () => {
          await twitterService.client.tweet(
            `@${message.username} Reminder: ${message}`,
            { reply: { in_reply_to_tweet_id: message.id } }
          );
        }, delay);
        
        await callback({
          text: `I'll remind you in ${amount} ${unit}! ⏰`,
          replyTo: message.id
        });
      }
    }
    
    return true;
  }
};

Quote Tweet Bot

Bot that quotes interesting tweets:

const quoteTweetBot = {
  name: "QuoteTweeter",
  description: "Quotes and comments on interesting tweets",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_TIMELINE_ALGORITHM: "weighted",
    TWITTER_TIMELINE_RELEVANCE_WEIGHT: "7",  // Prioritize relevant content
    TWITTER_TARGET_USERS: "sama,pmarca,naval,elonmusk"  // Quote these users
  }
};

// Quote tweet evaluation
const quoteEvaluator = {
  shouldQuote: (tweet: Tweet): boolean => {
    // Check if tweet is quotable
    if (tweet.text.length < 50) return false;  // Too short
    if (tweet.public_metrics.retweet_count < 10) return false;  // Not popular enough
    if (hasAlreadyQuoted(tweet.id)) return false;
    
    // Check content relevance
    const relevantKeywords = ['AI', 'future', 'technology', 'innovation'];
    return relevantKeywords.some(keyword => 
      tweet.text.toLowerCase().includes(keyword.toLowerCase())
    );
  },
  
  generateQuoteComment: async (tweet: Tweet, runtime: IAgentRuntime): Promise<string> => {
    const response = await runtime.generateText({
      messages: [{
        role: "system",
        content: "Add insightful commentary to this tweet. Be thoughtful and add value."
      }, {
        role: "user",
        content: tweet.text
      }],
      maxTokens: 100
    });
    
    return response.text;
  }
};

Search & Monitor Bots

Keyword Monitor

Bot that monitors specific keywords:

const keywordMonitor = {
  name: "KeywordTracker",
  description: "Monitors and responds to keyword mentions",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  keywords: ["#AIagents", "#ElizaOS", "autonomous agents", "AI automation"],
  settings: {
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_POST_ENABLE: "false"
  }
};

// Custom search action
const searchKeywordsAction: Action = {
  name: "SEARCH_KEYWORDS",
  description: "Search for specific keywords",
  
  handler: async (runtime, message, state, options, callback) => {
    const twitterService = runtime.getService('twitter');
    const keywords = runtime.character.keywords;
    
    for (const keyword of keywords) {
      const results = await twitterService.client.search(keyword, {
        max_results: 10,
        'tweet.fields': ['created_at', 'public_metrics', 'author_id']
      });
      
      for (const tweet of results.data || []) {
        // Process relevant tweets
        if (shouldEngageWith(tweet)) {
          await engageWithTweet(tweet, runtime);
        }
      }
    }
    
    return true;
  }
};

Hashtag Tracker

Bot that tracks trending hashtags:

const hashtagTracker = {
  name: "HashtagBot",
  description: "Tracks and engages with trending hashtags",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  trackedHashtags: ["#Web3", "#AI", "#BuildInPublic", "#100DaysOfCode"],
  settings: {
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_INTERACTION_INTERVAL_MIN: "30",  // Don't spam
    TWITTER_MAX_INTERACTIONS_PER_RUN: "5"    // Limit interactions
  }
};

User Monitor

Bot that monitors specific users:

const userMonitor = {
  name: "UserTracker",
  description: "Monitors and interacts with specific users",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_TARGET_USERS: "vitalikbuterin,balajis,cdixon",
    TWITTER_TIMELINE_ALGORITHM: "weighted",
    TWITTER_TIMELINE_USER_BASED_WEIGHT: "10",  // Heavily prioritize target users
    TWITTER_AUTO_RESPOND_MENTIONS: "false",    // Only interact with targets
    TWITTER_AUTO_RESPOND_REPLIES: "false"
  }
};

Advanced Bots

Full Engagement Bot

Bot with all features enabled:

const fullEngagementBot = {
  name: "FullEngagement",
  description: "Complete Twitter engagement bot",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  postExamples: [
    "What's the most underrated technology right now?",
    "Building in public update: Just crossed 1000 users!",
    "The best developers I know are constantly learning"
  ],
  settings: {
    // Posting
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_INTERVAL_MIN: "180",
    TWITTER_POST_INTERVAL_MAX: "360",
    
    // Interactions
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_AUTO_RESPOND_MENTIONS: "true",
    TWITTER_AUTO_RESPOND_REPLIES: "true",
    
    // Timeline processing
    TWITTER_ENABLE_ACTION_PROCESSING: "true",
    TWITTER_ACTION_INTERVAL: "240",
    
    // Algorithm configuration
    TWITTER_TIMELINE_ALGORITHM: "weighted",
    TWITTER_TIMELINE_USER_BASED_WEIGHT: "4",
    TWITTER_TIMELINE_TIME_BASED_WEIGHT: "3",
    TWITTER_TIMELINE_RELEVANCE_WEIGHT: "6"
  }
};

Multi-Account Bot

Managing multiple Twitter accounts:

const multiAccountSetup = async (runtime: IAgentRuntime) => {
  const twitterService = runtime.getService('twitter') as TwitterService;
  
  // Main account
  const mainAccount = await twitterService.createClient(
    runtime,
    'main-account',
    {
      TWITTER_API_KEY: process.env.MAIN_API_KEY,
      TWITTER_API_SECRET_KEY: process.env.MAIN_API_SECRET,
      TWITTER_ACCESS_TOKEN: process.env.MAIN_ACCESS_TOKEN,
      TWITTER_ACCESS_TOKEN_SECRET: process.env.MAIN_ACCESS_SECRET,
      TWITTER_POST_ENABLE: "true"
    }
  );
  
  // Support account
  const supportAccount = await twitterService.createClient(
    runtime,
    'support-account',
    {
      TWITTER_API_KEY: process.env.SUPPORT_API_KEY,
      TWITTER_API_SECRET_KEY: process.env.SUPPORT_API_SECRET,
      TWITTER_ACCESS_TOKEN: process.env.SUPPORT_ACCESS_TOKEN,
      TWITTER_ACCESS_TOKEN_SECRET: process.env.SUPPORT_ACCESS_SECRET,
      TWITTER_POST_ENABLE: "false",
      TWITTER_SEARCH_ENABLE: "true"
    }
  );
  
  // News account
  const newsAccount = await twitterService.createClient(
    runtime,
    'news-account',
    {
      TWITTER_API_KEY: process.env.NEWS_API_KEY,
      TWITTER_API_SECRET_KEY: process.env.NEWS_API_SECRET,
      TWITTER_ACCESS_TOKEN: process.env.NEWS_ACCESS_TOKEN,
      TWITTER_ACCESS_TOKEN_SECRET: process.env.NEWS_ACCESS_SECRET,
      TWITTER_POST_ENABLE: "true",
      TWITTER_POST_INTERVAL_MIN: "60"  // More frequent posts
    }
  );
  
  console.log('Multi-account setup complete!');
};

Analytics Bot

Bot that tracks and reports analytics:

const analyticsBot = {
  name: "AnalyticsBot",
  description: "Tracks Twitter performance metrics",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_POST_ENABLE: "true",
    TWITTER_SEARCH_ENABLE: "false"  // Focus on analytics
  }
};

// Analytics action
const analyticsAction: Action = {
  name: "TWITTER_ANALYTICS",
  description: "Generate Twitter analytics report",
  
  handler: async (runtime, message, state, options, callback) => {
    const twitterService = runtime.getService('twitter');
    const client = twitterService.getClient(runtime.agentId);
    
    // Get recent tweets
    const tweets = await client.client.getUserTweets(client.profile.id, {
      max_results: 100,
      'tweet.fields': ['created_at', 'public_metrics']
    });
    
    // Calculate metrics
    const metrics = {
      totalTweets: tweets.data.length,
      totalLikes: 0,
      totalRetweets: 0,
      totalReplies: 0,
      avgEngagement: 0
    };
    
    tweets.data.forEach(tweet => {
      metrics.totalLikes += tweet.public_metrics.like_count;
      metrics.totalRetweets += tweet.public_metrics.retweet_count;
      metrics.totalReplies += tweet.public_metrics.reply_count;
    });
    
    metrics.avgEngagement = 
      (metrics.totalLikes + metrics.totalRetweets + metrics.totalReplies) / 
      metrics.totalTweets;
    
    // Generate report
    const report = `
📊 Twitter Analytics Report

📝 Total Tweets: ${metrics.totalTweets}
❤️ Total Likes: ${metrics.totalLikes}
🔄 Total Retweets: ${metrics.totalRetweets}
💬 Total Replies: ${metrics.totalReplies}
📈 Avg Engagement: ${metrics.avgEngagement.toFixed(2)}

Top performing tweets coming in next thread...
    `;
    
    await callback({ text: report });
    return true;
  }
};

Testing Examples

Dry Run Bot

Test without actually posting:

const testBot = {
  name: "TestBot",
  description: "Bot for testing configurations",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  postExamples: [
    "This is a test tweet that won't actually post",
    "Testing the Twitter integration..."
  ],
  settings: {
    TWITTER_DRY_RUN: "true",  // Simulate all actions
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_IMMEDIATELY: "true",
    TWITTER_SEARCH_ENABLE: "true"
  }
};

// Monitor dry run output
runtime.on('twitter:dryRun', (action) => {
  console.log(`[DRY RUN] Would ${action.type}:`, action.content);
});

Debug Bot

Bot with extensive logging:

const debugBot = {
  name: "DebugBot",
  description: "Bot with debug logging enabled",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    DEBUG: "eliza:twitter:*",  // Enable all Twitter debug logs
    TWITTER_POST_ENABLE: "true",
    TWITTER_RETRY_LIMIT: "1",  // Fail fast for debugging
    TWITTER_POST_INTERVAL_MIN: "1"  // Quick testing
  }
};

Error Handling Examples

Resilient Bot

Bot with comprehensive error handling:

const resilientBot = {
  name: "ResilientBot",
  description: "Bot with robust error handling",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  settings: {
    TWITTER_RETRY_LIMIT: "5",
    TWITTER_POST_ENABLE: "true"
  }
};

// Error handling wrapper
const safeTwitterAction = (action: Action): Action => ({
  ...action,
  handler: async (runtime, message, state, options, callback) => {
    try {
      return await action.handler(runtime, message, state, options, callback);
    } catch (error) {
      runtime.logger.error(`Twitter action failed: ${action.name}`, error);
      
      // Handle specific errors
      if (error.code === 403) {
        await callback({
          text: "I don't have permission to do that. Please check my Twitter app permissions."
        });
      } else if (error.code === 429) {
        await callback({
          text: "I'm being rate limited. I'll try again later."
        });
      } else {
        await callback({
          text: "Something went wrong with Twitter. I'll try again soon."
        });
      }
      
      return false;
    }
  }
});

Integration Examples

With Other Platforms

import { discordPlugin } from '@elizaos/plugin-discord';
import { telegramPlugin } from '@elizaos/plugin-telegram';

const crossPlatformBot = {
  name: "CrossPlatform",
  description: "Bot that posts across platforms",
  plugins: [bootstrapPlugin, twitterPlugin, discordPlugin, telegramPlugin],
  clients: ["twitter", "discord", "telegram"],
  postExamples: [
    "New blog post: Understanding distributed systems",
    "What's your favorite programming language and why?"
  ],
  settings: {
    // Twitter settings
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_INTERVAL_MIN: "180",
    
    // Discord settings
    DISCORD_API_TOKEN: process.env.DISCORD_TOKEN,
    
    // Telegram settings
    TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_TOKEN
  }
};

// Cross-platform posting action
const crossPostAction: Action = {
  name: "CROSS_POST",
  description: "Post to all platforms",
  
  handler: async (runtime, message, state, options, callback) => {
    const content = options.content || "Hello from all platforms!";
    
    // Post to Twitter
    const twitterService = runtime.getService('twitter');
    await twitterService.client.tweet(content);
    
    // Post to Discord
    const discordService = runtime.getService('discord');
    await discordService.sendMessage(CHANNEL_ID, content);
    
    // Post to Telegram
    const telegramService = runtime.getService('telegram');
    await telegramService.sendMessage(CHAT_ID, content);
    
    await callback({
      text: "Posted to all platforms successfully!"
    });
    
    return true;
  }
};

Best Practices Example

Production Bot

Complete production-ready configuration:

import { twitterPlugin } from '@elizaos/plugin-twitter';
import { bootstrapPlugin } from '@elizaos/plugin-bootstrap';
import { AgentRuntime } from '@elizaos/core';

const productionBot = {
  name: "ProductionTwitterBot",
  description: "Production-ready Twitter bot",
  plugins: [bootstrapPlugin, twitterPlugin],
  clients: ["twitter"],
  
  // Diverse post examples
  postExamples: [
    // Questions to drive engagement
    "What's the biggest challenge you're facing in your project right now?",
    "If you could automate one thing in your workflow, what would it be?",
    
    // Insights and observations
    "The best code is the code you don't have to write",
    "Sometimes the simplest solution is the hardest to find",
    
    // Personal updates
    "Working on something exciting today. Can't wait to share more soon!",
    "Learning from yesterday's debugging session: always check the obvious first",
    
    // Threads
    "Thread: 5 lessons from building production systems 🧵\n\n1/",
    
    // Reactions to trends
    "Interesting to see how AI is changing the way we think about software development"
  ],
  
  settings: {
    // Credentials from environment
    TWITTER_API_KEY: process.env.TWITTER_API_KEY,
    TWITTER_API_SECRET_KEY: process.env.TWITTER_API_SECRET_KEY,
    TWITTER_ACCESS_TOKEN: process.env.TWITTER_ACCESS_TOKEN,
    TWITTER_ACCESS_TOKEN_SECRET: process.env.TWITTER_ACCESS_TOKEN_SECRET,
    
    // Conservative posting schedule
    TWITTER_POST_ENABLE: "true",
    TWITTER_POST_INTERVAL_MIN: "240",  // 4 hours
    TWITTER_POST_INTERVAL_MAX: "480",  // 8 hours
    TWITTER_POST_INTERVAL_VARIANCE: "0.2",
    
    // Moderate interaction settings
    TWITTER_SEARCH_ENABLE: "true",
    TWITTER_INTERACTION_INTERVAL_MIN: "30",
    TWITTER_INTERACTION_INTERVAL_MAX: "60",
    TWITTER_MAX_INTERACTIONS_PER_RUN: "5",
    
    // Quality over quantity
    TWITTER_TIMELINE_ALGORITHM: "weighted",
    TWITTER_TIMELINE_RELEVANCE_WEIGHT: "7",
    
    // Safety settings
    TWITTER_RETRY_LIMIT: "3",
    TWITTER_DRY_RUN: process.env.NODE_ENV === 'development' ? "true" : "false"
  }
};

// Initialize with monitoring
const runtime = new AgentRuntime({ character: productionBot });

// Add monitoring
runtime.on('error', (error) => {
  console.error('Runtime error:', error);
  // Send to monitoring service
});

runtime.on('twitter:post', (tweet) => {
  console.log('Posted tweet:', tweet.id);
  // Track metrics
});

runtime.on('twitter:rateLimit', (info) => {
  console.warn('Rate limit warning:', info);
  // Alert if critical
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  console.log('Shutting down gracefully...');
  await runtime.stop();
  process.exit(0);
});

// Start the bot
await runtime.start();
console.log('Production bot is running!');