Skip to content

Commit c64b95d

Browse files
authored
Merge pull request elizaOS#1036 from BalanaguYashwanth/656--fix-twitter-multi-agent-support
chore: improving client typing
2 parents ea1ef58 + 6cab7ef commit c64b95d

File tree

11 files changed

+20595
-18945
lines changed

11 files changed

+20595
-18945
lines changed

agent/src/index.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -338,22 +338,23 @@ export async function initializeClients(
338338
character.clients?.map((str) => str.toLowerCase()) || [];
339339
elizaLogger.log("initializeClients", clientTypes, "for", character.name);
340340

341-
if (clientTypes.includes("auto")) {
341+
if (clientTypes.includes(Clients.DIRECT)) {
342342
const autoClient = await AutoClientInterface.start(runtime);
343343
if (autoClient) clients.auto = autoClient;
344344
}
345345

346-
if (clientTypes.includes("discord")) {
346+
if (clientTypes.includes(Clients.DISCORD)) {
347347
const discordClient = await DiscordClientInterface.start(runtime);
348348
if (discordClient) clients.discord = discordClient;
349349
}
350350

351-
if (clientTypes.includes("telegram")) {
351+
if (clientTypes.includes(Clients.TELEGRAM)) {
352352
const telegramClient = await TelegramClientInterface.start(runtime);
353353
if (telegramClient) clients.telegram = telegramClient;
354354
}
355355

356-
if (clientTypes.includes("twitter")) {
356+
if (clientTypes.includes(Clients.TWITTER)) {
357+
TwitterClientInterface.enableSearch = !isFalsish(getSecret(character, "TWITTER_SEARCH_ENABLE"));
357358
const twitterClient = await TwitterClientInterface.start(runtime);
358359
// TODO: This might be incorrect, please test if you are concerned about this functionality
359360
// By default we have disabled this because it is annoying for users
@@ -363,7 +364,7 @@ export async function initializeClients(
363364
if (twitterClient) clients.twitter = twitterClient;
364365
}
365366

366-
if (clientTypes.includes("farcaster")) {
367+
if (clientTypes.includes(Clients.FARCASTER)) {
367368
// why is this one different :(
368369
const farcasterClient = new FarcasterAgentClient(runtime);
369370
if (farcasterClient) {

docs/docs/packages/agent.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,16 @@ export async function initializeClients(
138138
const clients = [];
139139
const clientTypes = character.clients?.map((str) => str.toLowerCase()) || [];
140140

141-
if (clientTypes.includes("discord")) {
141+
if (clientTypes.includes(Clients.DISCORD)) {
142142
clients.push(await DiscordClientInterface.start(runtime));
143143
}
144-
if (clientTypes.includes("telegram")) {
144+
if (clientTypes.includes(Clients.TELEGRAM)) {
145145
clients.push(await TelegramClientInterface.start(runtime));
146146
}
147-
if (clientTypes.includes("twitter")) {
147+
if (clientTypes.includes(Clients.TWITTER)) {
148148
clients.push(await TwitterClientInterface.start(runtime));
149149
}
150-
if (clientTypes.includes("auto")) {
150+
if (clientTypes.includes(Clients.DIRECT)) {
151151
clients.push(await AutoClientInterface.start(runtime));
152152
}
153153

docs/docs/packages/agents.md

+4-8
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,16 @@ export async function initializeClients(
101101
const clients = [];
102102
const clientTypes = character.clients?.map((str) => str.toLowerCase()) || [];
103103

104-
// Initialize requested clients
105-
if (clientTypes.includes("discord")) {
104+
if (clientTypes.includes(Clients.DISCORD)) {
106105
clients.push(await DiscordClientInterface.start(runtime));
107106
}
108-
109-
if (clientTypes.includes("telegram")) {
107+
if (clientTypes.includes(Clients.TELEGRAM)) {
110108
clients.push(await TelegramClientInterface.start(runtime));
111109
}
112-
113-
if (clientTypes.includes("twitter")) {
110+
if (clientTypes.includes(Clients.TWITTER)) {
114111
clients.push(await TwitterClientInterface.start(runtime));
115112
}
116-
117-
if (clientTypes.includes("auto")) {
113+
if (clientTypes.includes(Clients.DIRECT)) {
118114
clients.push(await AutoClientInterface.start(runtime));
119115
}
120116

docs/docs/packages/clients.md

-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ The Twitter client enables posting, searching, and interacting with Twitter user
134134

135135
```typescript
136136
import { TwitterClientInterface } from "@eliza/client-twitter";
137-
138137
// Initialize client
139138
const client = await TwitterClientInterface.start(runtime);
140139

packages/client-twitter/src/base.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,18 @@ export class ClientBase extends EventEmitter {
155155
async init() {
156156
//test
157157
const username = this.runtime.getSetting("TWITTER_USERNAME");
158+
const password = this.runtime.getSetting("TWITTER_PASSWORD");
159+
const email = this.runtime.getSetting("TWITTER_EMAIL");
160+
const twitter2faSecret = this.runtime.getSetting("TWITTER_2FA_SECRET") || undefined;
161+
const cookies = this.runtime.getSetting("TWITTER_COOKIES");
162+
158163

159164
if (!username) {
160165
throw new Error("Twitter username not configured");
161166
}
162167
// Check for Twitter cookies
163-
if (this.runtime.getSetting("TWITTER_COOKIES")) {
164-
const cookiesArray = JSON.parse(
165-
this.runtime.getSetting("TWITTER_COOKIES")
166-
);
168+
if (cookies) {
169+
const cookiesArray = JSON.parse(cookies);
167170

168171
await this.setCookiesFromArray(cookiesArray);
169172
} else {
@@ -186,9 +189,9 @@ export class ClientBase extends EventEmitter {
186189
try {
187190
await this.twitterClient.login(
188191
username,
189-
this.runtime.getSetting("TWITTER_PASSWORD"),
190-
this.runtime.getSetting("TWITTER_EMAIL"),
191-
this.runtime.getSetting("TWITTER_2FA_SECRET") || undefined
192+
password,
193+
email,
194+
twitter2faSecret
192195
);
193196
} catch (error) {
194197
elizaLogger.error(`Login attempt failed: ${error.message}`);
@@ -480,10 +483,11 @@ export class ClientBase extends EventEmitter {
480483
}
481484

482485
const timeline = await this.fetchHomeTimeline(cachedTimeline ? 10 : 50);
486+
const username = this.runtime.getSetting("TWITTER_USERNAME");
483487

484488
// Get the most recent 20 mentions and interactions
485489
const mentionsAndInteractions = await this.fetchSearchTweets(
486-
`@${this.runtime.getSetting("TWITTER_USERNAME")}`,
490+
`@${username}`,
487491
20,
488492
SearchMode.Latest
489493
);

packages/client-twitter/src/environment.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export async function validateTwitterConfig(
2323
runtime: IAgentRuntime
2424
): Promise<TwitterConfig> {
2525
try {
26-
const config = {
26+
const twitterConfig = {
2727
TWITTER_DRY_RUN:
2828
runtime.getSetting("TWITTER_DRY_RUN") ||
2929
process.env.TWITTER_DRY_RUN ||
@@ -46,7 +46,7 @@ export async function validateTwitterConfig(
4646
DEFAULT_MAX_TWEET_LENGTH.toString(),
4747
};
4848

49-
return twitterEnvSchema.parse(config);
49+
return twitterEnvSchema.parse(twitterConfig);
5050
} catch (error) {
5151
if (error instanceof z.ZodError) {
5252
const errorMessages = error.errors

packages/client-twitter/src/interactions.ts

+15-17
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Here is the current post text again. Remember to include an action if the curren
5353
{{currentPost}}
5454
` + messageCompletionFooter;
5555

56-
export const twitterShouldRespondTemplate = (targetUsersStr: string) =>
56+
export const twitterShouldRespondTemplate = (targetUsersStr: string) =>
5757
`# INSTRUCTIONS: Determine if {{agentName}} (@{{twitterUserName}}) should respond to the message and participate in the conversation. Do not comment. Just respond with "true" or "false".
5858
5959
Response options are RESPOND, IGNORE and STOP.
@@ -89,7 +89,6 @@ Thread of Tweets You Are Replying To:
8989
export class TwitterInteractionClient {
9090
client: ClientBase;
9191
runtime: IAgentRuntime;
92-
9392
constructor(client: ClientBase, runtime: IAgentRuntime) {
9493
this.client = client;
9594
this.runtime = runtime;
@@ -133,7 +132,7 @@ export class TwitterInteractionClient {
133132
.filter(u => u.length > 0); // Filter out empty strings after split
134133

135134
elizaLogger.log("Processing target users:", TARGET_USERS);
136-
135+
137136
if (TARGET_USERS.length > 0) {
138137
// Create a map to store tweets by user
139138
const tweetsByUser = new Map<string, Tweet[]>();
@@ -142,24 +141,23 @@ export class TwitterInteractionClient {
142141
for (const username of TARGET_USERS) {
143142
try {
144143
const userTweets = (await this.client.twitterClient.fetchSearchTweets(
145-
`from:${username}`,
146-
3,
147-
SearchMode.Latest
144+
`from:${username}`,
145+
3,
146+
SearchMode.Latest
148147
)).tweets;
149148

150149
// Filter for unprocessed, non-reply, recent tweets
151150
const validTweets = userTweets.filter(tweet => {
152-
const isUnprocessed = !this.client.lastCheckedTweetId ||
153-
parseInt(tweet.id) > this.client.lastCheckedTweetId;
151+
const isUnprocessed = !this.client.lastCheckedTweetId || parseInt(tweet.id) > this.client.lastCheckedTweetId;
154152
const isRecent = (Date.now() - (tweet.timestamp * 1000)) < 2 * 60 * 60 * 1000;
155-
153+
156154
elizaLogger.log(`Tweet ${tweet.id} checks:`, {
157155
isUnprocessed,
158156
isRecent,
159157
isReply: tweet.isReply,
160158
isRetweet: tweet.isRetweet
161159
});
162-
160+
163161
return isUnprocessed && !tweet.isReply && !tweet.isRetweet && isRecent;
164162
});
165163

@@ -191,7 +189,7 @@ export class TwitterInteractionClient {
191189
elizaLogger.log("No target users configured, processing only mentions");
192190
}
193191

194-
192+
195193

196194
// Sort tweet candidates by ID in ascending order
197195
uniqueTweetCandidates
@@ -359,17 +357,17 @@ export class TwitterInteractionClient {
359357
const targetUsersStr = this.runtime.getSetting("TWITTER_TARGET_USERS");
360358

361359
// 2. Process the string to get valid usernames
362-
const validTargetUsersStr = targetUsersStr && targetUsersStr.trim()
360+
const validTargetUsersStr = targetUsersStr && targetUsersStr.trim()
363361
? targetUsersStr.split(',') // Split by commas: "user1,user2" -> ["user1", "user2"]
364362
.map(u => u.trim()) // Remove whitespace: [" user1 ", "user2 "] -> ["user1", "user2"]
365-
.filter(u => u.length > 0)
366-
.join(',')
367-
: '';
363+
.filter(u => u.length > 0)
364+
.join(',')
365+
: '';
368366

369367
const shouldRespondContext = composeContext({
370368
state,
371-
template: this.runtime.character.templates?.twitterShouldRespondTemplate?.(validTargetUsersStr) ||
372-
this.runtime.character?.templates?.shouldRespondTemplate ||
369+
template: this.runtime.character.templates?.twitterShouldRespondTemplate?.(validTargetUsersStr) ||
370+
this.runtime.character?.templates?.shouldRespondTemplate ||
373371
twitterShouldRespondTemplate(validTargetUsersStr),
374372
});
375373

packages/client-twitter/src/post.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ function truncateToCompleteSentence(
9696
export class TwitterPostClient {
9797
client: ClientBase;
9898
runtime: IAgentRuntime;
99+
twitterUsername: string;
99100
private isProcessing: boolean = false;
100101
private lastProcessTime: number = 0;
101102
private stopProcessingActions: boolean = false;
@@ -111,7 +112,7 @@ export class TwitterPostClient {
111112
timestamp: number;
112113
}>(
113114
"twitter/" +
114-
this.runtime.getSetting("TWITTER_USERNAME") +
115+
this.twitterUsername +
115116
"/lastPost"
116117
);
117118

@@ -136,7 +137,6 @@ export class TwitterPostClient {
136137
elizaLogger.log(`Next tweet scheduled in ${randomMinutes} minutes`);
137138
};
138139

139-
140140
const processActionsLoop = async () => {
141141
const actionInterval = parseInt(
142142
this.runtime.getSetting("ACTION_INTERVAL")
@@ -185,11 +185,13 @@ export class TwitterPostClient {
185185
} else {
186186
elizaLogger.log("Action processing loop disabled by configuration");
187187
}
188+
generateNewTweetLoop();
188189
}
189190

190191
constructor(client: ClientBase, runtime: IAgentRuntime) {
191192
this.client = client;
192193
this.runtime = runtime;
194+
this.twitterUsername = runtime.getSetting("TWITTER_USERNAME");
193195
}
194196

195197
private async generateNewTweet() {
@@ -310,7 +312,7 @@ export class TwitterPostClient {
310312
userId: this.client.profile.id,
311313
inReplyToStatusId:
312314
tweetResult.legacy.in_reply_to_status_id_str,
313-
permanentUrl: `https://twitter.com/${this.runtime.getSetting("TWITTER_USERNAME")}/status/${tweetResult.rest_id}`,
315+
permanentUrl: `https://twitter.com/${this.twitterUsername}/status/${tweetResult.rest_id}`,
314316
hashtags: [],
315317
mentions: [],
316318
photos: [],
@@ -432,7 +434,7 @@ export class TwitterPostClient {
432434

433435
await this.runtime.ensureUserExists(
434436
this.runtime.agentId,
435-
this.runtime.getSetting("TWITTER_USERNAME"),
437+
this.twitterUsername,
436438
this.runtime.character.name,
437439
"twitter"
438440
);
@@ -463,7 +465,7 @@ export class TwitterPostClient {
463465
content: { text: "", action: "" },
464466
},
465467
{
466-
twitterUserName: this.runtime.getSetting("TWITTER_USERNAME"),
468+
twitterUserName: this.twitterUsername,
467469
currentTweet: `ID: ${tweet.id}\nFrom: ${tweet.name} (@${tweet.username})\nText: ${tweet.text}`,
468470
}
469471
);
@@ -549,7 +551,7 @@ export class TwitterPostClient {
549551
content: { text: tweet.text, action: "QUOTE" }
550552
},
551553
{
552-
twitterUserName: this.runtime.getSetting("TWITTER_USERNAME"),
554+
twitterUserName: this.twitterUsername,
553555
currentPost: `From @${tweet.username}: ${tweet.text}`,
554556
formattedConversation,
555557
imageContext: imageDescriptions.length > 0
@@ -698,7 +700,7 @@ export class TwitterPostClient {
698700
content: { text: tweet.text, action: "" }
699701
},
700702
{
701-
twitterUserName: this.runtime.getSetting("TWITTER_USERNAME"),
703+
twitterUserName: this.twitterUsername,
702704
currentPost: `From @${tweet.username}: ${tweet.text}`,
703705
formattedConversation,
704706
imageContext: imageDescriptions.length > 0

0 commit comments

Comments
 (0)