Twitter Integration

Twitter Integration

Configuration

Configure your Twitter client using environment variables and the twitterConfigSchema:

// Environment variables
TWITTER_USERNAME="your-username"
TWITTER_PASSWORD="your-password"
TWITTER_EMAIL="your-email"
TWITTER_2FA_SECRET="optional-2fa-secret"
TWITTER_POST_INTERVAL_HOURS=4
TWITTER_POLLING_INTERVAL=5 # minutes
TWITTER_DRY_RUN=true # For testing

// Configuration schema
const twitterConfigSchema = z.object({
  username: z.string().min(1, "Twitter username is required"),
  password: z.string().min(1, "Twitter password is required"),
  email: z.string().email("Valid email is required"),
  twoFactorSecret: z.string().optional(),
  retryLimit: z.number().int().min(1).default(5),
  postIntervalHours: z.number().int().min(1).default(4),
  enableActions: z.boolean().default(false)
});

Setting Up the Client

Initialize and start the Twitter client with your agent:

import { TwitterClient } from "@liz/twitter-client";

const config = {
	username: process.env.TWITTER_USERNAME,
	password: process.env.TWITTER_PASSWORD,
	email: process.env.TWITTER_EMAIL,
	twoFactorSecret: process.env.TWITTER_2FA_SECRET,
	retryLimit: 3,
	postIntervalHours: 4,
	pollingInterval: 5,
	dryRun: process.env.NODE_ENV !== "production",
};

const twitter = new TwitterClient(agent, config);
await twitter.start(); // Starts posting & monitoring intervals

Automated Posting

The client can automatically generate and post tweets at regular intervals:

// Automatic posting loop
async generateAndPost() {
  const responseText = await this.fetchTweetContent({
    agentId: this.agent.getAgentId(),
    userId: "twitter_client",
    roomId: "twitter",
    text: "<SYSTEM> Generate a new tweet to post on your timeline </SYSTEM>",
    type: "text"
  });

  const tweets = await sendThreadedTweet(this, responseText);

  // Store tweets in memory
  for (const tweet of tweets) {
    await storeTweetIfNotExists({
      id: tweet.id,
      text: tweet.text,
      userId: this.config.username,
      username: this.config.username,
      conversationId: tweet.conversationId,
      permanentUrl: tweet.permanentUrl
    });
  }
}

Mention Monitoring

Monitor and respond to mentions automatically:

// Check for new mentions
async checkInteractions() {
  const mentions = await this.getMentions();
  for (const mention of mentions) {
    if (mention.id <= this.lastCheckedTweetId) continue;
    await this.handleMention(mention);
    this.lastCheckedTweetId = mention.id;
  }
}

// Handle mention with agent
async handleMention(tweet) {
  const responseText = await this.fetchTweetContent({
    agentId: this.agent.getAgentId(),
    userId: `tw_user_${tweet.userId}`,
    roomId: tweet.conversationId || "twitter",
    text: `@${tweet.username}: ${tweet.text}`,
    type: "text"
  });

  const replies = await sendThreadedTweet(this, responseText, tweet.id);
}

Thread Management

Handle tweet threads and conversations:

// Split long content into tweets
function splitTweetContent(text, maxLength = 280) {
	if (text.length <= maxLength) return [text];

	const tweets = [];
	const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];

	let currentTweet = "";
	for (const sentence of sentences) {
		if ((currentTweet + sentence).length <= maxLength) {
			currentTweet += sentence;
		} else {
			tweets.push(currentTweet.trim());
			currentTweet = sentence;
		}
	}

	if (currentTweet) tweets.push(currentTweet.trim());
	return tweets;
}

// Send threaded tweets
async function sendThreadedTweet(client, content, replyToId) {
	const tweets = [];
	const parts = splitTweetContent(content);
	let lastTweetId = replyToId;

	for (const part of parts) {
		const tweet = await client.sendTweet(part, lastTweetId);
		tweets.push(tweet);
		lastTweetId = tweet.id;
		await new Promise((resolve) => setTimeout(resolve, 1000));
	}

	return tweets;
}

Memory Integration

Store tweets and maintain conversation context:

// Store tweet in database
async function storeTweetIfNotExists(tweet) {
	const exists = await prisma.tweet.count({
		where: { id: tweet.id },
	});

	if (!exists) {
		await prisma.tweet.create({
			data: {
				id: tweet.id,
				text: tweet.text,
				userId: tweet.userId,
				username: tweet.username,
				conversationId: tweet.conversationId,
				inReplyToId: tweet.inReplyToId,
				permanentUrl: tweet.permanentUrl,
			},
		});
		return true;
	}
	return false;
}

// Get conversation thread
async function getTweetThread(conversationId) {
	return prisma.tweet.findMany({
		where: { conversationId },
		orderBy: { createdAt: "asc" },
	});
}

Best Practices

Rate Limiting

  • Use RequestQueue for API calls

  • Add delays between tweets

  • Handle API errors gracefully

  • Implement exponential backoff

Testing

  • Use dryRun mode for testing

  • Monitor tweet content

  • Test thread splitting

  • Verify mention handling

Next: Examples →

Last updated