@elizaos/plugin-twitter 1.2.20 → 1.2.21
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.
- package/dist/index.js +573 -485
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,18 +5,18 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
import { logger as
|
|
8
|
+
import { logger as logger12 } from "@elizaos/core";
|
|
9
9
|
|
|
10
10
|
// src/services/twitter.service.ts
|
|
11
|
-
import { Service, logger as
|
|
11
|
+
import { Service, logger as logger10 } from "@elizaos/core";
|
|
12
12
|
|
|
13
13
|
// src/interactions.ts
|
|
14
14
|
import {
|
|
15
|
-
ChannelType,
|
|
15
|
+
ChannelType as ChannelType2,
|
|
16
16
|
EventType,
|
|
17
17
|
ModelType,
|
|
18
|
-
createUniqueUuid as
|
|
19
|
-
logger as
|
|
18
|
+
createUniqueUuid as createUniqueUuid3,
|
|
19
|
+
logger as logger5
|
|
20
20
|
} from "@elizaos/core";
|
|
21
21
|
|
|
22
22
|
// src/client/auth.ts
|
|
@@ -6299,6 +6299,142 @@ function getEpochMs(ts) {
|
|
|
6299
6299
|
return ts;
|
|
6300
6300
|
}
|
|
6301
6301
|
|
|
6302
|
+
// src/utils/memory.ts
|
|
6303
|
+
import {
|
|
6304
|
+
ChannelType,
|
|
6305
|
+
createUniqueUuid as createUniqueUuid2,
|
|
6306
|
+
logger as logger4
|
|
6307
|
+
} from "@elizaos/core";
|
|
6308
|
+
async function ensureTwitterContext(runtime, options) {
|
|
6309
|
+
const {
|
|
6310
|
+
userId,
|
|
6311
|
+
username,
|
|
6312
|
+
name = username,
|
|
6313
|
+
conversationId = userId
|
|
6314
|
+
} = options;
|
|
6315
|
+
const worldId = createUniqueUuid2(runtime, userId);
|
|
6316
|
+
const roomId = createUniqueUuid2(runtime, conversationId);
|
|
6317
|
+
const entityId = createUniqueUuid2(runtime, userId);
|
|
6318
|
+
try {
|
|
6319
|
+
await runtime.ensureWorldExists({
|
|
6320
|
+
id: worldId,
|
|
6321
|
+
name: `${username}'s Twitter`,
|
|
6322
|
+
agentId: runtime.agentId,
|
|
6323
|
+
serverId: userId,
|
|
6324
|
+
metadata: {
|
|
6325
|
+
ownership: { ownerId: userId },
|
|
6326
|
+
twitter: {
|
|
6327
|
+
username,
|
|
6328
|
+
id: userId
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
});
|
|
6332
|
+
await runtime.ensureRoomExists({
|
|
6333
|
+
id: roomId,
|
|
6334
|
+
name: `Twitter conversation ${conversationId}`,
|
|
6335
|
+
source: "twitter",
|
|
6336
|
+
type: ChannelType.FEED,
|
|
6337
|
+
channelId: conversationId,
|
|
6338
|
+
serverId: userId,
|
|
6339
|
+
worldId
|
|
6340
|
+
});
|
|
6341
|
+
await runtime.ensureConnection({
|
|
6342
|
+
entityId,
|
|
6343
|
+
roomId,
|
|
6344
|
+
userName: username,
|
|
6345
|
+
name,
|
|
6346
|
+
source: "twitter",
|
|
6347
|
+
type: ChannelType.FEED,
|
|
6348
|
+
worldId
|
|
6349
|
+
});
|
|
6350
|
+
return {
|
|
6351
|
+
worldId,
|
|
6352
|
+
roomId,
|
|
6353
|
+
entityId
|
|
6354
|
+
};
|
|
6355
|
+
} catch (error) {
|
|
6356
|
+
logger4.error("Failed to ensure Twitter context:", error);
|
|
6357
|
+
throw new Error(`Failed to create Twitter context for user ${username}: ${error.message}`);
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
async function createMemorySafe(runtime, memory, tableName = "messages", maxRetries = 3) {
|
|
6361
|
+
let lastError = null;
|
|
6362
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
6363
|
+
try {
|
|
6364
|
+
await runtime.createMemory(memory, tableName);
|
|
6365
|
+
return;
|
|
6366
|
+
} catch (error) {
|
|
6367
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
6368
|
+
logger4.warn(`Failed to create memory (attempt ${attempt + 1}/${maxRetries}):`, error);
|
|
6369
|
+
if (error.message?.includes("duplicate") || error.message?.includes("constraint")) {
|
|
6370
|
+
logger4.debug("Memory already exists, skipping");
|
|
6371
|
+
return;
|
|
6372
|
+
}
|
|
6373
|
+
if (attempt < maxRetries - 1) {
|
|
6374
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
logger4.error(`Failed to create memory after ${maxRetries} attempts:`, lastError);
|
|
6379
|
+
throw lastError;
|
|
6380
|
+
}
|
|
6381
|
+
async function isTweetProcessed(runtime, tweetId) {
|
|
6382
|
+
try {
|
|
6383
|
+
const memoryId = createUniqueUuid2(runtime, tweetId);
|
|
6384
|
+
const memory = await runtime.getMemoryById(memoryId);
|
|
6385
|
+
return !!memory;
|
|
6386
|
+
} catch (error) {
|
|
6387
|
+
logger4.debug(`Error checking if tweet ${tweetId} is processed:`, error);
|
|
6388
|
+
return false;
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
async function getRecentTweets(runtime, username, count = 10) {
|
|
6392
|
+
try {
|
|
6393
|
+
const cacheKey = `twitter/${username}/recentTweets`;
|
|
6394
|
+
const cached = await runtime.getCache(cacheKey);
|
|
6395
|
+
if (cached && Array.isArray(cached)) {
|
|
6396
|
+
return cached;
|
|
6397
|
+
}
|
|
6398
|
+
return [];
|
|
6399
|
+
} catch (error) {
|
|
6400
|
+
logger4.debug("Error getting recent tweets from cache:", error);
|
|
6401
|
+
return [];
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6404
|
+
async function addToRecentTweets(runtime, username, tweetText, maxRecent = 10) {
|
|
6405
|
+
try {
|
|
6406
|
+
const cacheKey = `twitter/${username}/recentTweets`;
|
|
6407
|
+
const recent = await getRecentTweets(runtime, username, maxRecent);
|
|
6408
|
+
recent.unshift(tweetText);
|
|
6409
|
+
const trimmed = recent.slice(0, maxRecent);
|
|
6410
|
+
await runtime.setCache(cacheKey, trimmed);
|
|
6411
|
+
} catch (error) {
|
|
6412
|
+
logger4.debug("Error updating recent tweets cache:", error);
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6415
|
+
async function isDuplicateTweet(runtime, username, tweetText, similarityThreshold = 0.9) {
|
|
6416
|
+
try {
|
|
6417
|
+
const recentTweets = await getRecentTweets(runtime, username);
|
|
6418
|
+
if (recentTweets.includes(tweetText)) {
|
|
6419
|
+
return true;
|
|
6420
|
+
}
|
|
6421
|
+
const normalizedNew = tweetText.toLowerCase().trim();
|
|
6422
|
+
for (const recent of recentTweets) {
|
|
6423
|
+
const normalizedRecent = recent.toLowerCase().trim();
|
|
6424
|
+
if (normalizedNew === normalizedRecent) {
|
|
6425
|
+
return true;
|
|
6426
|
+
}
|
|
6427
|
+
if (normalizedNew.includes(normalizedRecent) || normalizedRecent.includes(normalizedNew)) {
|
|
6428
|
+
return true;
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
return false;
|
|
6432
|
+
} catch (error) {
|
|
6433
|
+
logger4.debug("Error checking for duplicate tweets:", error);
|
|
6434
|
+
return false;
|
|
6435
|
+
}
|
|
6436
|
+
}
|
|
6437
|
+
|
|
6302
6438
|
// src/interactions.ts
|
|
6303
6439
|
var TwitterInteractionClient = class {
|
|
6304
6440
|
/**
|
|
@@ -6325,12 +6461,12 @@ var TwitterInteractionClient = class {
|
|
|
6325
6461
|
this.isRunning = true;
|
|
6326
6462
|
const handleTwitterInteractionsLoop = () => {
|
|
6327
6463
|
if (!this.isRunning) {
|
|
6328
|
-
|
|
6464
|
+
logger5.info("Twitter interaction client stopped, exiting loop");
|
|
6329
6465
|
return;
|
|
6330
6466
|
}
|
|
6331
6467
|
const engagementIntervalMinutes = getRandomInterval(this.runtime, "engagement");
|
|
6332
6468
|
const interactionInterval = engagementIntervalMinutes * 60 * 1e3;
|
|
6333
|
-
|
|
6469
|
+
logger5.info(
|
|
6334
6470
|
`Twitter interaction client will check in ${engagementIntervalMinutes.toFixed(1)} minutes`
|
|
6335
6471
|
);
|
|
6336
6472
|
this.handleTwitterInteractions();
|
|
@@ -6344,14 +6480,14 @@ var TwitterInteractionClient = class {
|
|
|
6344
6480
|
* Stops the Twitter interaction client
|
|
6345
6481
|
*/
|
|
6346
6482
|
async stop() {
|
|
6347
|
-
|
|
6483
|
+
logger5.log("Stopping Twitter interaction client...");
|
|
6348
6484
|
this.isRunning = false;
|
|
6349
6485
|
}
|
|
6350
6486
|
/**
|
|
6351
6487
|
* Asynchronously handles Twitter interactions by checking for mentions and target user posts.
|
|
6352
6488
|
*/
|
|
6353
6489
|
async handleTwitterInteractions() {
|
|
6354
|
-
|
|
6490
|
+
logger5.log("Checking Twitter interactions");
|
|
6355
6491
|
const twitterUsername = this.client.profile?.username;
|
|
6356
6492
|
try {
|
|
6357
6493
|
const repliesEnabled = (getSetting(this.runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
|
|
@@ -6363,9 +6499,9 @@ var TwitterInteractionClient = class {
|
|
|
6363
6499
|
await this.handleTargetUserPosts(targetUsersConfig);
|
|
6364
6500
|
}
|
|
6365
6501
|
await this.client.cacheLatestCheckedTweetId();
|
|
6366
|
-
|
|
6502
|
+
logger5.log("Finished checking Twitter interactions");
|
|
6367
6503
|
} catch (error) {
|
|
6368
|
-
|
|
6504
|
+
logger5.error("Error handling Twitter interactions:", error);
|
|
6369
6505
|
}
|
|
6370
6506
|
}
|
|
6371
6507
|
/**
|
|
@@ -6389,7 +6525,7 @@ var TwitterInteractionClient = class {
|
|
|
6389
6525
|
}
|
|
6390
6526
|
await this.processMentionTweets(mentionCandidates);
|
|
6391
6527
|
} catch (error) {
|
|
6392
|
-
|
|
6528
|
+
logger5.error("Error handling mentions:", error);
|
|
6393
6529
|
}
|
|
6394
6530
|
}
|
|
6395
6531
|
/**
|
|
@@ -6401,7 +6537,7 @@ var TwitterInteractionClient = class {
|
|
|
6401
6537
|
if (targetUsers.length === 0 && !targetUsersConfig.includes("*")) {
|
|
6402
6538
|
return;
|
|
6403
6539
|
}
|
|
6404
|
-
|
|
6540
|
+
logger5.info(
|
|
6405
6541
|
`Checking posts from target users: ${targetUsers.join(", ") || "everyone (*)"}`
|
|
6406
6542
|
);
|
|
6407
6543
|
for (const targetUser of targetUsers) {
|
|
@@ -6415,7 +6551,7 @@ var TwitterInteractionClient = class {
|
|
|
6415
6551
|
1 /* Latest */
|
|
6416
6552
|
);
|
|
6417
6553
|
if (searchResult.tweets.length > 0) {
|
|
6418
|
-
|
|
6554
|
+
logger5.info(
|
|
6419
6555
|
`Found ${searchResult.tweets.length} posts from @${normalizedUsername}`
|
|
6420
6556
|
);
|
|
6421
6557
|
await this.processTargetUserTweets(
|
|
@@ -6424,14 +6560,14 @@ var TwitterInteractionClient = class {
|
|
|
6424
6560
|
);
|
|
6425
6561
|
}
|
|
6426
6562
|
} catch (error) {
|
|
6427
|
-
|
|
6563
|
+
logger5.error(`Error searching posts from @${targetUser}:`, error);
|
|
6428
6564
|
}
|
|
6429
6565
|
}
|
|
6430
6566
|
if (targetUsersConfig.includes("*")) {
|
|
6431
6567
|
await this.processTimelineForEngagement();
|
|
6432
6568
|
}
|
|
6433
6569
|
} catch (error) {
|
|
6434
|
-
|
|
6570
|
+
logger5.error("Error handling target user posts:", error);
|
|
6435
6571
|
}
|
|
6436
6572
|
}
|
|
6437
6573
|
/**
|
|
@@ -6444,12 +6580,11 @@ var TwitterInteractionClient = class {
|
|
|
6444
6580
|
let engagementCount = 0;
|
|
6445
6581
|
for (const tweet of tweets) {
|
|
6446
6582
|
if (engagementCount >= maxEngagementsPerRun) {
|
|
6447
|
-
|
|
6583
|
+
logger5.info(`Reached max engagements limit (${maxEngagementsPerRun})`);
|
|
6448
6584
|
break;
|
|
6449
6585
|
}
|
|
6450
|
-
const
|
|
6451
|
-
|
|
6452
|
-
if (existingMemory) {
|
|
6586
|
+
const isProcessed = await isTweetProcessed(this.runtime, tweet.id);
|
|
6587
|
+
if (isProcessed) {
|
|
6453
6588
|
continue;
|
|
6454
6589
|
}
|
|
6455
6590
|
const tweetAge = Date.now() - getEpochMs(tweet.timestamp);
|
|
@@ -6459,7 +6594,7 @@ var TwitterInteractionClient = class {
|
|
|
6459
6594
|
}
|
|
6460
6595
|
const shouldEngage = await this.shouldEngageWithTweet(tweet);
|
|
6461
6596
|
if (shouldEngage) {
|
|
6462
|
-
|
|
6597
|
+
logger5.info(
|
|
6463
6598
|
`Engaging with tweet from @${username}: ${tweet.text.substring(0, 50)}...`
|
|
6464
6599
|
);
|
|
6465
6600
|
await this.ensureTweetContext(tweet);
|
|
@@ -6485,13 +6620,13 @@ var TwitterInteractionClient = class {
|
|
|
6485
6620
|
return tweetAge < 12 * 60 * 60 * 1e3;
|
|
6486
6621
|
});
|
|
6487
6622
|
if (relevantTweets.length > 0) {
|
|
6488
|
-
|
|
6623
|
+
logger5.info(
|
|
6489
6624
|
`Found ${relevantTweets.length} relevant tweets from timeline`
|
|
6490
6625
|
);
|
|
6491
6626
|
await this.processTargetUserTweets(relevantTweets, "timeline");
|
|
6492
6627
|
}
|
|
6493
6628
|
} catch (error) {
|
|
6494
|
-
|
|
6629
|
+
logger5.error("Error processing timeline for engagement:", error);
|
|
6495
6630
|
}
|
|
6496
6631
|
}
|
|
6497
6632
|
/**
|
|
@@ -6509,10 +6644,10 @@ var TwitterInteractionClient = class {
|
|
|
6509
6644
|
}
|
|
6510
6645
|
};
|
|
6511
6646
|
const shouldEngageMemory = {
|
|
6512
|
-
id:
|
|
6647
|
+
id: createUniqueUuid3(this.runtime, `eval-${tweet.id}`),
|
|
6513
6648
|
entityId: this.runtime.agentId,
|
|
6514
6649
|
agentId: this.runtime.agentId,
|
|
6515
|
-
roomId:
|
|
6650
|
+
roomId: createUniqueUuid3(this.runtime, tweet.conversationId),
|
|
6516
6651
|
content: {
|
|
6517
6652
|
text: `Should I engage with this tweet? Tweet: "${tweet.text}" by @${tweet.username}`,
|
|
6518
6653
|
evaluationContext
|
|
@@ -6544,7 +6679,7 @@ Response (YES/NO):`;
|
|
|
6544
6679
|
});
|
|
6545
6680
|
return response.trim().toUpperCase().includes("YES");
|
|
6546
6681
|
} catch (error) {
|
|
6547
|
-
|
|
6682
|
+
logger5.error("Error determining engagement:", error);
|
|
6548
6683
|
return false;
|
|
6549
6684
|
}
|
|
6550
6685
|
}
|
|
@@ -6552,57 +6687,31 @@ Response (YES/NO):`;
|
|
|
6552
6687
|
* Ensure tweet context exists (world, room, entity)
|
|
6553
6688
|
*/
|
|
6554
6689
|
async ensureTweetContext(tweet) {
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
worldId
|
|
6581
|
-
});
|
|
6582
|
-
const entityId = createUniqueUuid2(this.runtime, userId);
|
|
6583
|
-
await this.runtime.ensureConnection({
|
|
6584
|
-
entityId,
|
|
6585
|
-
roomId,
|
|
6586
|
-
userName: username,
|
|
6587
|
-
name: tweet.name,
|
|
6588
|
-
source: "twitter",
|
|
6589
|
-
type: ChannelType.FEED,
|
|
6590
|
-
worldId
|
|
6591
|
-
});
|
|
6592
|
-
const tweetMemory = {
|
|
6593
|
-
id: createUniqueUuid2(this.runtime, tweet.id),
|
|
6594
|
-
entityId,
|
|
6595
|
-
content: {
|
|
6596
|
-
text: tweet.text,
|
|
6597
|
-
url: tweet.permanentUrl,
|
|
6598
|
-
source: "twitter",
|
|
6599
|
-
tweet
|
|
6600
|
-
},
|
|
6601
|
-
agentId: this.runtime.agentId,
|
|
6602
|
-
roomId,
|
|
6603
|
-
createdAt: getEpochMs(tweet.timestamp)
|
|
6604
|
-
};
|
|
6605
|
-
await this.runtime.createMemory(tweetMemory, "messages");
|
|
6690
|
+
try {
|
|
6691
|
+
const context = await ensureTwitterContext(this.runtime, {
|
|
6692
|
+
userId: tweet.userId,
|
|
6693
|
+
username: tweet.username,
|
|
6694
|
+
name: tweet.name,
|
|
6695
|
+
conversationId: tweet.conversationId || tweet.id
|
|
6696
|
+
});
|
|
6697
|
+
const tweetMemory = {
|
|
6698
|
+
id: createUniqueUuid3(this.runtime, tweet.id),
|
|
6699
|
+
entityId: context.entityId,
|
|
6700
|
+
content: {
|
|
6701
|
+
text: tweet.text,
|
|
6702
|
+
url: tweet.permanentUrl,
|
|
6703
|
+
source: "twitter",
|
|
6704
|
+
tweet
|
|
6705
|
+
},
|
|
6706
|
+
agentId: this.runtime.agentId,
|
|
6707
|
+
roomId: context.roomId,
|
|
6708
|
+
createdAt: getEpochMs(tweet.timestamp)
|
|
6709
|
+
};
|
|
6710
|
+
await createMemorySafe(this.runtime, tweetMemory, "messages");
|
|
6711
|
+
} catch (error) {
|
|
6712
|
+
logger5.error(`Failed to ensure context for tweet ${tweet.id}:`, error);
|
|
6713
|
+
throw error;
|
|
6714
|
+
}
|
|
6606
6715
|
}
|
|
6607
6716
|
/**
|
|
6608
6717
|
* Engage with a tweet by generating and sending a reply
|
|
@@ -6610,15 +6719,15 @@ Response (YES/NO):`;
|
|
|
6610
6719
|
async engageWithTweet(tweet) {
|
|
6611
6720
|
try {
|
|
6612
6721
|
const message = {
|
|
6613
|
-
id:
|
|
6614
|
-
entityId:
|
|
6722
|
+
id: createUniqueUuid3(this.runtime, tweet.id),
|
|
6723
|
+
entityId: createUniqueUuid3(this.runtime, tweet.userId),
|
|
6615
6724
|
content: {
|
|
6616
6725
|
text: tweet.text,
|
|
6617
6726
|
source: "twitter",
|
|
6618
6727
|
tweet
|
|
6619
6728
|
},
|
|
6620
6729
|
agentId: this.runtime.agentId,
|
|
6621
|
-
roomId:
|
|
6730
|
+
roomId: createUniqueUuid3(this.runtime, tweet.conversationId),
|
|
6622
6731
|
createdAt: getEpochMs(tweet.timestamp)
|
|
6623
6732
|
};
|
|
6624
6733
|
const result = await this.handleTweet({
|
|
@@ -6628,7 +6737,7 @@ Response (YES/NO):`;
|
|
|
6628
6737
|
});
|
|
6629
6738
|
return result.text && result.text.length > 0;
|
|
6630
6739
|
} catch (error) {
|
|
6631
|
-
|
|
6740
|
+
logger5.error("Error engaging with tweet:", error);
|
|
6632
6741
|
return false;
|
|
6633
6742
|
}
|
|
6634
6743
|
}
|
|
@@ -6643,7 +6752,7 @@ Response (YES/NO):`;
|
|
|
6643
6752
|
* Note: MENTION_RECEIVED is currently disabled (see TODO below)
|
|
6644
6753
|
*/
|
|
6645
6754
|
async processMentionTweets(mentionCandidates) {
|
|
6646
|
-
|
|
6755
|
+
logger5.log(
|
|
6647
6756
|
"Completed checking mentioned tweets:",
|
|
6648
6757
|
mentionCandidates.length
|
|
6649
6758
|
);
|
|
@@ -6657,7 +6766,7 @@ Response (YES/NO):`;
|
|
|
6657
6766
|
targetUsersConfig
|
|
6658
6767
|
);
|
|
6659
6768
|
if (!shouldTarget) {
|
|
6660
|
-
|
|
6769
|
+
logger5.log(
|
|
6661
6770
|
`Skipping tweet from @${tweet.username} - not in target users list`
|
|
6662
6771
|
);
|
|
6663
6772
|
}
|
|
@@ -6671,18 +6780,18 @@ Response (YES/NO):`;
|
|
|
6671
6780
|
0,
|
|
6672
6781
|
maxInteractionsPerRun
|
|
6673
6782
|
);
|
|
6674
|
-
|
|
6783
|
+
logger5.info(
|
|
6675
6784
|
`Processing ${tweetsToProcess.length} of ${uniqueTweetCandidates.length} mention tweets (max: ${maxInteractionsPerRun})`
|
|
6676
6785
|
);
|
|
6677
6786
|
for (const tweet of tweetsToProcess) {
|
|
6678
6787
|
if (!this.client.lastCheckedTweetId || BigInt(tweet.id) > this.client.lastCheckedTweetId) {
|
|
6679
|
-
const tweetId =
|
|
6788
|
+
const tweetId = createUniqueUuid3(this.runtime, tweet.id);
|
|
6680
6789
|
const existingResponse = await this.runtime.getMemoryById(tweetId);
|
|
6681
6790
|
if (existingResponse) {
|
|
6682
|
-
|
|
6791
|
+
logger5.log(`Already responded to tweet ${tweet.id}, skipping`);
|
|
6683
6792
|
continue;
|
|
6684
6793
|
}
|
|
6685
|
-
const conversationRoomId =
|
|
6794
|
+
const conversationRoomId = createUniqueUuid3(
|
|
6686
6795
|
this.runtime,
|
|
6687
6796
|
tweet.conversationId
|
|
6688
6797
|
);
|
|
@@ -6696,23 +6805,23 @@ Response (YES/NO):`;
|
|
|
6696
6805
|
(memory2) => memory2.content.inReplyTo === tweetId || memory2.content.inReplyTo === tweet.id
|
|
6697
6806
|
);
|
|
6698
6807
|
if (hasExistingReply) {
|
|
6699
|
-
|
|
6808
|
+
logger5.log(
|
|
6700
6809
|
`Already responded to tweet ${tweet.id} (found in conversation history), skipping`
|
|
6701
6810
|
);
|
|
6702
6811
|
continue;
|
|
6703
6812
|
}
|
|
6704
|
-
|
|
6813
|
+
logger5.log("New Tweet found", tweet.id);
|
|
6705
6814
|
const userId = tweet.userId;
|
|
6706
6815
|
const conversationId = tweet.conversationId || tweet.id;
|
|
6707
|
-
const roomId =
|
|
6816
|
+
const roomId = createUniqueUuid3(this.runtime, conversationId);
|
|
6708
6817
|
const username = tweet.username;
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
const worldId =
|
|
6818
|
+
logger5.log("----");
|
|
6819
|
+
logger5.log(`User: ${username} (${userId})`);
|
|
6820
|
+
logger5.log(`Tweet: ${tweet.id}`);
|
|
6821
|
+
logger5.log(`Conversation: ${conversationId}`);
|
|
6822
|
+
logger5.log(`Room: ${roomId}`);
|
|
6823
|
+
logger5.log("----");
|
|
6824
|
+
const worldId = createUniqueUuid3(this.runtime, userId);
|
|
6716
6825
|
await this.runtime.ensureWorldExists({
|
|
6717
6826
|
id: worldId,
|
|
6718
6827
|
name: `${username}'s Twitter`,
|
|
@@ -6726,21 +6835,21 @@ Response (YES/NO):`;
|
|
|
6726
6835
|
}
|
|
6727
6836
|
}
|
|
6728
6837
|
});
|
|
6729
|
-
const entityId =
|
|
6838
|
+
const entityId = createUniqueUuid3(this.runtime, userId);
|
|
6730
6839
|
await this.runtime.ensureConnection({
|
|
6731
6840
|
entityId,
|
|
6732
6841
|
roomId,
|
|
6733
6842
|
userName: username,
|
|
6734
6843
|
name: tweet.name,
|
|
6735
6844
|
source: "twitter",
|
|
6736
|
-
type:
|
|
6845
|
+
type: ChannelType2.FEED,
|
|
6737
6846
|
worldId
|
|
6738
6847
|
});
|
|
6739
6848
|
await this.runtime.ensureRoomExists({
|
|
6740
6849
|
id: roomId,
|
|
6741
6850
|
name: `Twitter conversation ${conversationId}`,
|
|
6742
6851
|
source: "twitter",
|
|
6743
|
-
type:
|
|
6852
|
+
type: ChannelType2.FEED,
|
|
6744
6853
|
channelId: conversationId,
|
|
6745
6854
|
serverId: userId,
|
|
6746
6855
|
worldId
|
|
@@ -6758,11 +6867,11 @@ Response (YES/NO):`;
|
|
|
6758
6867
|
roomId,
|
|
6759
6868
|
createdAt: getEpochMs(tweet.timestamp)
|
|
6760
6869
|
};
|
|
6761
|
-
|
|
6762
|
-
await this.runtime
|
|
6870
|
+
logger5.log("Saving tweet memory...");
|
|
6871
|
+
await createMemorySafe(this.runtime, memory, "messages");
|
|
6763
6872
|
if (tweet.thread && tweet.thread.length > 0) {
|
|
6764
6873
|
const threadStartId = tweet.thread[0].id;
|
|
6765
|
-
const threadMemoryId =
|
|
6874
|
+
const threadMemoryId = createUniqueUuid3(
|
|
6766
6875
|
this.runtime,
|
|
6767
6876
|
`thread-${threadStartId}`
|
|
6768
6877
|
);
|
|
@@ -6813,15 +6922,15 @@ Response (YES/NO):`;
|
|
|
6813
6922
|
interaction.userId,
|
|
6814
6923
|
interaction.targetTweet.conversationId
|
|
6815
6924
|
);
|
|
6816
|
-
await this.runtime
|
|
6925
|
+
await createMemorySafe(this.runtime, memory, "messages");
|
|
6817
6926
|
const reactionMessage = {
|
|
6818
|
-
id:
|
|
6927
|
+
id: createUniqueUuid3(this.runtime, interaction.targetTweetId),
|
|
6819
6928
|
content: {
|
|
6820
6929
|
text: interaction.targetTweet.text,
|
|
6821
6930
|
source: "twitter"
|
|
6822
6931
|
},
|
|
6823
|
-
entityId:
|
|
6824
|
-
roomId:
|
|
6932
|
+
entityId: createUniqueUuid3(this.runtime, interaction.userId),
|
|
6933
|
+
roomId: createUniqueUuid3(
|
|
6825
6934
|
this.runtime,
|
|
6826
6935
|
interaction.targetTweet.conversationId
|
|
6827
6936
|
),
|
|
@@ -6872,7 +6981,7 @@ Response (YES/NO):`;
|
|
|
6872
6981
|
callback: async () => [],
|
|
6873
6982
|
reaction: {
|
|
6874
6983
|
type: "quote",
|
|
6875
|
-
entityId:
|
|
6984
|
+
entityId: createUniqueUuid3(this.runtime, interaction.userId)
|
|
6876
6985
|
},
|
|
6877
6986
|
source: "twitter"
|
|
6878
6987
|
};
|
|
@@ -6882,12 +6991,12 @@ Response (YES/NO):`;
|
|
|
6882
6991
|
}
|
|
6883
6992
|
this.runtime.emitEvent(EventType.REACTION_RECEIVED, {
|
|
6884
6993
|
runtime: this.runtime,
|
|
6885
|
-
entityId:
|
|
6886
|
-
roomId:
|
|
6994
|
+
entityId: createUniqueUuid3(this.runtime, interaction.userId),
|
|
6995
|
+
roomId: createUniqueUuid3(
|
|
6887
6996
|
this.runtime,
|
|
6888
6997
|
interaction.targetTweet.conversationId
|
|
6889
6998
|
),
|
|
6890
|
-
world:
|
|
6999
|
+
world: createUniqueUuid3(this.runtime, interaction.userId),
|
|
6891
7000
|
message: reactionMessage,
|
|
6892
7001
|
source: "twitter",
|
|
6893
7002
|
metadata: {
|
|
@@ -6913,10 +7022,10 @@ Response (YES/NO):`;
|
|
|
6913
7022
|
*/
|
|
6914
7023
|
createMemoryObject(type, id, userId, conversationId) {
|
|
6915
7024
|
return {
|
|
6916
|
-
id:
|
|
7025
|
+
id: createUniqueUuid3(this.runtime, id),
|
|
6917
7026
|
agentId: this.runtime.agentId,
|
|
6918
|
-
entityId:
|
|
6919
|
-
roomId:
|
|
7027
|
+
entityId: createUniqueUuid3(this.runtime, userId),
|
|
7028
|
+
roomId: createUniqueUuid3(this.runtime, conversationId),
|
|
6920
7029
|
content: {
|
|
6921
7030
|
type,
|
|
6922
7031
|
source: "twitter"
|
|
@@ -6941,23 +7050,23 @@ Response (YES/NO):`;
|
|
|
6941
7050
|
thread
|
|
6942
7051
|
}) {
|
|
6943
7052
|
if (!message.content.text) {
|
|
6944
|
-
|
|
7053
|
+
logger5.log("Skipping Tweet with no text", tweet.id);
|
|
6945
7054
|
return { text: "", actions: ["IGNORE"] };
|
|
6946
7055
|
}
|
|
6947
7056
|
const callback = async (response2, tweetId) => {
|
|
6948
7057
|
try {
|
|
6949
7058
|
if (!response2.text) {
|
|
6950
|
-
|
|
7059
|
+
logger5.warn("No text content in response, skipping tweet reply");
|
|
6951
7060
|
return [];
|
|
6952
7061
|
}
|
|
6953
7062
|
const tweetToReplyTo = tweetId || tweet.id;
|
|
6954
7063
|
if (this.isDryRun) {
|
|
6955
|
-
|
|
7064
|
+
logger5.info(
|
|
6956
7065
|
`[DRY RUN] Would have replied to ${tweet.username} with: ${response2.text}`
|
|
6957
7066
|
);
|
|
6958
7067
|
return [];
|
|
6959
7068
|
}
|
|
6960
|
-
|
|
7069
|
+
logger5.info(`Replying to tweet ${tweetToReplyTo}`);
|
|
6961
7070
|
const tweetResult = await sendTweet(
|
|
6962
7071
|
this.client,
|
|
6963
7072
|
response2.text,
|
|
@@ -6967,7 +7076,7 @@ Response (YES/NO):`;
|
|
|
6967
7076
|
if (!tweetResult) {
|
|
6968
7077
|
throw new Error("Failed to get tweet result from response");
|
|
6969
7078
|
}
|
|
6970
|
-
const responseId =
|
|
7079
|
+
const responseId = createUniqueUuid3(this.runtime, tweetResult.id);
|
|
6971
7080
|
const responseMemory = {
|
|
6972
7081
|
id: responseId,
|
|
6973
7082
|
entityId: this.runtime.agentId,
|
|
@@ -6980,21 +7089,21 @@ Response (YES/NO):`;
|
|
|
6980
7089
|
},
|
|
6981
7090
|
createdAt: Date.now()
|
|
6982
7091
|
};
|
|
6983
|
-
await this.runtime
|
|
7092
|
+
await createMemorySafe(this.runtime, responseMemory, "messages");
|
|
6984
7093
|
return [responseMemory];
|
|
6985
7094
|
} catch (error) {
|
|
6986
|
-
|
|
7095
|
+
logger5.error("Error in tweet reply callback:", error);
|
|
6987
7096
|
return [];
|
|
6988
7097
|
}
|
|
6989
7098
|
};
|
|
6990
7099
|
const twitterUserId = tweet.userId;
|
|
6991
|
-
const entityId =
|
|
7100
|
+
const entityId = createUniqueUuid3(this.runtime, twitterUserId);
|
|
6992
7101
|
const twitterUsername = tweet.username;
|
|
6993
7102
|
this.runtime.emitEvent(
|
|
6994
7103
|
["TWITTER_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */, EventType.MESSAGE_RECEIVED],
|
|
6995
7104
|
{
|
|
6996
7105
|
runtime: this.runtime,
|
|
6997
|
-
world:
|
|
7106
|
+
world: createUniqueUuid3(this.runtime, twitterUserId),
|
|
6998
7107
|
entityId,
|
|
6999
7108
|
roomId: message.roomId,
|
|
7000
7109
|
userId: twitterUserId,
|
|
@@ -7022,9 +7131,9 @@ Response (YES/NO):`;
|
|
|
7022
7131
|
|
|
7023
7132
|
// src/post.ts
|
|
7024
7133
|
import {
|
|
7025
|
-
ChannelType as
|
|
7026
|
-
createUniqueUuid as
|
|
7027
|
-
logger as
|
|
7134
|
+
ChannelType as ChannelType3,
|
|
7135
|
+
createUniqueUuid as createUniqueUuid4,
|
|
7136
|
+
logger as logger6,
|
|
7028
7137
|
ModelType as ModelType2
|
|
7029
7138
|
} from "@elizaos/core";
|
|
7030
7139
|
var TwitterPostClient = class {
|
|
@@ -7043,42 +7152,42 @@ var TwitterPostClient = class {
|
|
|
7043
7152
|
this.runtime = runtime;
|
|
7044
7153
|
const dryRunSetting = this.state?.TWITTER_DRY_RUN ?? getSetting(this.runtime, "TWITTER_DRY_RUN") ?? process.env.TWITTER_DRY_RUN;
|
|
7045
7154
|
this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
|
|
7046
|
-
|
|
7047
|
-
|
|
7155
|
+
logger6.log("Twitter Post Client Configuration:");
|
|
7156
|
+
logger6.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
|
|
7048
7157
|
const postIntervalMin = parseInt(
|
|
7049
7158
|
this.state?.TWITTER_POST_INTERVAL_MIN || getSetting(this.runtime, "TWITTER_POST_INTERVAL_MIN") || process.env.TWITTER_POST_INTERVAL_MIN || "90"
|
|
7050
7159
|
);
|
|
7051
7160
|
const postIntervalMax = parseInt(
|
|
7052
7161
|
this.state?.TWITTER_POST_INTERVAL_MAX || getSetting(this.runtime, "TWITTER_POST_INTERVAL_MAX") || process.env.TWITTER_POST_INTERVAL_MAX || "150"
|
|
7053
7162
|
);
|
|
7054
|
-
|
|
7163
|
+
logger6.log(`- Post Interval: ${postIntervalMin}-${postIntervalMax} minutes (randomized)`);
|
|
7055
7164
|
}
|
|
7056
7165
|
/**
|
|
7057
7166
|
* Stops the Twitter post client
|
|
7058
7167
|
*/
|
|
7059
7168
|
async stop() {
|
|
7060
|
-
|
|
7169
|
+
logger6.log("Stopping Twitter post client...");
|
|
7061
7170
|
this.isRunning = false;
|
|
7062
7171
|
}
|
|
7063
7172
|
/**
|
|
7064
7173
|
* Starts the Twitter post client, setting up a loop to periodically generate new tweets.
|
|
7065
7174
|
*/
|
|
7066
7175
|
async start() {
|
|
7067
|
-
|
|
7176
|
+
logger6.log("Starting Twitter post client...");
|
|
7068
7177
|
this.isRunning = true;
|
|
7069
7178
|
const generateNewTweetLoop = async () => {
|
|
7070
7179
|
if (!this.isRunning) {
|
|
7071
|
-
|
|
7180
|
+
logger6.log("Twitter post client stopped, exiting loop");
|
|
7072
7181
|
return;
|
|
7073
7182
|
}
|
|
7074
7183
|
await this.generateNewTweet();
|
|
7075
7184
|
if (!this.isRunning) {
|
|
7076
|
-
|
|
7185
|
+
logger6.log("Twitter post client stopped after tweet, exiting loop");
|
|
7077
7186
|
return;
|
|
7078
7187
|
}
|
|
7079
7188
|
const postIntervalMinutes = getRandomInterval(this.runtime, "post");
|
|
7080
7189
|
const interval = postIntervalMinutes * 60 * 1e3;
|
|
7081
|
-
|
|
7190
|
+
logger6.info(`Next tweet scheduled in ${postIntervalMinutes.toFixed(1)} minutes`);
|
|
7082
7191
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
7083
7192
|
if (this.isRunning) {
|
|
7084
7193
|
generateNewTweetLoop();
|
|
@@ -7087,7 +7196,7 @@ var TwitterPostClient = class {
|
|
|
7087
7196
|
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
7088
7197
|
const postImmediately = this.state?.TWITTER_POST_IMMEDIATELY || getSetting(this.runtime, "TWITTER_POST_IMMEDIATELY") || process.env.TWITTER_POST_IMMEDIATELY;
|
|
7089
7198
|
if (postImmediately === "true" || postImmediately === true) {
|
|
7090
|
-
|
|
7199
|
+
logger6.info(
|
|
7091
7200
|
"TWITTER_POST_IMMEDIATELY is true, generating initial tweet now"
|
|
7092
7201
|
);
|
|
7093
7202
|
let retries = 0;
|
|
@@ -7095,7 +7204,7 @@ var TwitterPostClient = class {
|
|
|
7095
7204
|
const success = await this.generateNewTweet();
|
|
7096
7205
|
if (success) break;
|
|
7097
7206
|
retries++;
|
|
7098
|
-
|
|
7207
|
+
logger6.info(`Retrying immediate tweet (attempt ${retries}/5)...`);
|
|
7099
7208
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
7100
7209
|
}
|
|
7101
7210
|
}
|
|
@@ -7107,24 +7216,24 @@ var TwitterPostClient = class {
|
|
|
7107
7216
|
* @returns {Promise<boolean>} true if tweet was posted successfully
|
|
7108
7217
|
*/
|
|
7109
7218
|
async generateNewTweet() {
|
|
7110
|
-
|
|
7219
|
+
logger6.info("Attempting to generate new tweet...");
|
|
7111
7220
|
if (this.isPosting) {
|
|
7112
|
-
|
|
7221
|
+
logger6.info("Already posting a tweet, skipping concurrent attempt");
|
|
7113
7222
|
return false;
|
|
7114
7223
|
}
|
|
7115
7224
|
this.isPosting = true;
|
|
7116
7225
|
try {
|
|
7117
7226
|
const userId = this.client.profile?.id;
|
|
7118
7227
|
if (!userId) {
|
|
7119
|
-
|
|
7228
|
+
logger6.error("Cannot generate tweet: Twitter profile not available");
|
|
7120
7229
|
this.isPosting = false;
|
|
7121
7230
|
return false;
|
|
7122
7231
|
}
|
|
7123
|
-
|
|
7232
|
+
logger6.info(
|
|
7124
7233
|
`Generating tweet for user: ${this.client.profile?.username} (${userId})`
|
|
7125
7234
|
);
|
|
7126
|
-
const worldId =
|
|
7127
|
-
const roomId =
|
|
7235
|
+
const worldId = createUniqueUuid4(this.runtime, userId);
|
|
7236
|
+
const roomId = createUniqueUuid4(this.runtime, `${userId}-home`);
|
|
7128
7237
|
const state = await this.runtime.composeState({
|
|
7129
7238
|
agentId: this.runtime.agentId,
|
|
7130
7239
|
entityId: this.runtime.agentId,
|
|
@@ -7132,7 +7241,7 @@ var TwitterPostClient = class {
|
|
|
7132
7241
|
content: { text: "", type: "post" },
|
|
7133
7242
|
createdAt: Date.now()
|
|
7134
7243
|
}).catch((error) => {
|
|
7135
|
-
|
|
7244
|
+
logger6.warn("Error composing state, using minimal state:", error);
|
|
7136
7245
|
return {
|
|
7137
7246
|
agentId: this.runtime.agentId,
|
|
7138
7247
|
recentMemories: [],
|
|
@@ -7153,7 +7262,7 @@ ${this.runtime.character.messageExamples.map(
|
|
|
7153
7262
|
|
|
7154
7263
|
Style guidelines:
|
|
7155
7264
|
- Be authentic, opinionated, and specific - no generic platitudes
|
|
7156
|
-
- Use your unique voice and perspective
|
|
7265
|
+
- Use your unique voice and perspective
|
|
7157
7266
|
- Share hot takes, unpopular opinions, or specific insights
|
|
7158
7267
|
- Be conversational, not preachy
|
|
7159
7268
|
- If you use emojis, use them sparingly and purposefully
|
|
@@ -7180,15 +7289,15 @@ Generate a single tweet that sounds like YOU would actually write it:`;
|
|
|
7180
7289
|
);
|
|
7181
7290
|
const tweetText = generatedContent.trim();
|
|
7182
7291
|
if (!tweetText || tweetText.length === 0) {
|
|
7183
|
-
|
|
7292
|
+
logger6.error("Generated empty tweet content");
|
|
7184
7293
|
return false;
|
|
7185
7294
|
}
|
|
7186
7295
|
if (tweetText.includes("Error: Missing")) {
|
|
7187
|
-
|
|
7296
|
+
logger6.error("Error in generated content:", tweetText);
|
|
7188
7297
|
return false;
|
|
7189
7298
|
}
|
|
7190
7299
|
if (tweetText.length > 280) {
|
|
7191
|
-
|
|
7300
|
+
logger6.warn(`Generated tweet too long (${tweetText.length} chars), truncating...`);
|
|
7192
7301
|
const sentences = tweetText.match(/[^.!?]+[.!?]+/g) || [tweetText];
|
|
7193
7302
|
let truncated = "";
|
|
7194
7303
|
for (const sentence of sentences) {
|
|
@@ -7199,73 +7308,67 @@ Generate a single tweet that sounds like YOU would actually write it:`;
|
|
|
7199
7308
|
}
|
|
7200
7309
|
}
|
|
7201
7310
|
const finalTweet = truncated.trim() || tweetText.substring(0, 277) + "...";
|
|
7202
|
-
|
|
7311
|
+
logger6.info(`Truncated tweet: ${finalTweet}`);
|
|
7203
7312
|
if (this.isDryRun) {
|
|
7204
|
-
|
|
7313
|
+
logger6.info(`[DRY RUN] Would post tweet: ${finalTweet}`);
|
|
7205
7314
|
return false;
|
|
7206
7315
|
}
|
|
7207
7316
|
const result2 = await this.postToTwitter(finalTweet, []);
|
|
7208
7317
|
if (result2 === null) {
|
|
7209
|
-
|
|
7318
|
+
logger6.info("Skipped posting duplicate tweet");
|
|
7210
7319
|
return false;
|
|
7211
7320
|
}
|
|
7212
7321
|
const tweetId2 = result2.id;
|
|
7213
|
-
|
|
7214
|
-
|
|
7322
|
+
logger6.info(`Tweet posted successfully! ID: ${tweetId2}`);
|
|
7323
|
+
logger6.info("Tweet posted successfully (memory saving disabled due to room constraints)");
|
|
7215
7324
|
return true;
|
|
7216
7325
|
}
|
|
7217
|
-
|
|
7326
|
+
logger6.info(`Generated tweet: ${tweetText}`);
|
|
7218
7327
|
if (this.isDryRun) {
|
|
7219
|
-
|
|
7328
|
+
logger6.info(`[DRY RUN] Would post tweet: ${tweetText}`);
|
|
7220
7329
|
return false;
|
|
7221
7330
|
}
|
|
7222
7331
|
const result = await this.postToTwitter(tweetText, []);
|
|
7223
7332
|
if (result === null) {
|
|
7224
|
-
|
|
7333
|
+
logger6.info("Skipped posting duplicate tweet");
|
|
7225
7334
|
return false;
|
|
7226
7335
|
}
|
|
7227
7336
|
const tweetId = result.id;
|
|
7228
|
-
|
|
7337
|
+
logger6.info(`Tweet posted successfully! ID: ${tweetId}`);
|
|
7229
7338
|
if (result) {
|
|
7230
|
-
const postedTweetId =
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
}
|
|
7260
|
-
},
|
|
7261
|
-
createdAt: Date.now()
|
|
7262
|
-
};
|
|
7263
|
-
await this.runtime.createMemory(postedMemory, "messages");
|
|
7264
|
-
logger5.info("Tweet posted and saved to memory successfully");
|
|
7339
|
+
const postedTweetId = createUniqueUuid4(this.runtime, tweetId);
|
|
7340
|
+
try {
|
|
7341
|
+
const context = await ensureTwitterContext(this.runtime, {
|
|
7342
|
+
userId,
|
|
7343
|
+
username: this.client.profile?.username || "unknown",
|
|
7344
|
+
conversationId: `${userId}-home`
|
|
7345
|
+
});
|
|
7346
|
+
const postedMemory = {
|
|
7347
|
+
id: postedTweetId,
|
|
7348
|
+
entityId: this.runtime.agentId,
|
|
7349
|
+
agentId: this.runtime.agentId,
|
|
7350
|
+
roomId: context.roomId,
|
|
7351
|
+
content: {
|
|
7352
|
+
text: tweetText,
|
|
7353
|
+
source: "twitter",
|
|
7354
|
+
channelType: ChannelType3.FEED,
|
|
7355
|
+
type: "post",
|
|
7356
|
+
metadata: {
|
|
7357
|
+
tweetId,
|
|
7358
|
+
postedAt: Date.now()
|
|
7359
|
+
}
|
|
7360
|
+
},
|
|
7361
|
+
createdAt: Date.now()
|
|
7362
|
+
};
|
|
7363
|
+
await createMemorySafe(this.runtime, postedMemory, "messages");
|
|
7364
|
+
logger6.info("Tweet posted and saved to memory successfully");
|
|
7365
|
+
} catch (error) {
|
|
7366
|
+
logger6.error("Failed to save tweet memory:", error);
|
|
7367
|
+
}
|
|
7265
7368
|
return true;
|
|
7266
7369
|
}
|
|
7267
7370
|
} catch (error) {
|
|
7268
|
-
|
|
7371
|
+
logger6.error("Error generating tweet:", error);
|
|
7269
7372
|
return false;
|
|
7270
7373
|
} finally {
|
|
7271
7374
|
this.isPosting = false;
|
|
@@ -7279,42 +7382,33 @@ Generate a single tweet that sounds like YOU would actually write it:`;
|
|
|
7279
7382
|
*/
|
|
7280
7383
|
async postToTwitter(text, mediaData = []) {
|
|
7281
7384
|
try {
|
|
7282
|
-
const
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
return null;
|
|
7292
|
-
}
|
|
7385
|
+
const username = this.client.profile?.username;
|
|
7386
|
+
if (!username) {
|
|
7387
|
+
logger6.error("No profile username available");
|
|
7388
|
+
return null;
|
|
7389
|
+
}
|
|
7390
|
+
const isDuplicate = await isDuplicateTweet(this.runtime, username, text);
|
|
7391
|
+
if (isDuplicate) {
|
|
7392
|
+
logger6.warn("Tweet is a duplicate of a recent post. Skipping to avoid duplicate.");
|
|
7393
|
+
return null;
|
|
7293
7394
|
}
|
|
7294
7395
|
const mediaIds = [];
|
|
7295
7396
|
if (mediaData && mediaData.length > 0) {
|
|
7296
7397
|
for (const media of mediaData) {
|
|
7297
7398
|
try {
|
|
7298
|
-
|
|
7399
|
+
logger6.warn(
|
|
7299
7400
|
"Media upload not currently supported with the modern Twitter API"
|
|
7300
7401
|
);
|
|
7301
7402
|
} catch (error) {
|
|
7302
|
-
|
|
7403
|
+
logger6.error("Error uploading media:", error);
|
|
7303
7404
|
}
|
|
7304
7405
|
}
|
|
7305
7406
|
}
|
|
7306
7407
|
const result = await sendTweet(this.client, text, mediaData);
|
|
7307
|
-
await this.runtime
|
|
7308
|
-
`twitter/${this.client.profile?.username}/lastPost`,
|
|
7309
|
-
{
|
|
7310
|
-
id: result.id,
|
|
7311
|
-
text,
|
|
7312
|
-
timestamp: Date.now()
|
|
7313
|
-
}
|
|
7314
|
-
);
|
|
7408
|
+
await addToRecentTweets(this.runtime, username, text);
|
|
7315
7409
|
return result;
|
|
7316
7410
|
} catch (error) {
|
|
7317
|
-
|
|
7411
|
+
logger6.error("Error posting to Twitter:", error);
|
|
7318
7412
|
throw error;
|
|
7319
7413
|
}
|
|
7320
7414
|
}
|
|
@@ -7322,13 +7416,13 @@ Generate a single tweet that sounds like YOU would actually write it:`;
|
|
|
7322
7416
|
|
|
7323
7417
|
// src/timeline.ts
|
|
7324
7418
|
import {
|
|
7325
|
-
ChannelType as
|
|
7419
|
+
ChannelType as ChannelType4,
|
|
7326
7420
|
composePromptFromState,
|
|
7327
|
-
createUniqueUuid as
|
|
7421
|
+
createUniqueUuid as createUniqueUuid5,
|
|
7328
7422
|
ModelType as ModelType3,
|
|
7329
7423
|
parseKeyValueXml
|
|
7330
7424
|
} from "@elizaos/core";
|
|
7331
|
-
import { logger as
|
|
7425
|
+
import { logger as logger7 } from "@elizaos/core";
|
|
7332
7426
|
|
|
7333
7427
|
// src/templates.ts
|
|
7334
7428
|
var twitterActionTemplate = `
|
|
@@ -7408,18 +7502,18 @@ var TwitterTimelineClient = class {
|
|
|
7408
7502
|
this.timelineType = timelineMode === "following" /* Following */ ? "following" /* Following */ : "foryou" /* ForYou */;
|
|
7409
7503
|
}
|
|
7410
7504
|
async start() {
|
|
7411
|
-
|
|
7505
|
+
logger7.info("Starting Twitter timeline client...");
|
|
7412
7506
|
this.isRunning = true;
|
|
7413
7507
|
const handleTwitterTimelineLoop = () => {
|
|
7414
7508
|
if (!this.isRunning) {
|
|
7415
|
-
|
|
7509
|
+
logger7.info("Twitter timeline client stopped, exiting loop");
|
|
7416
7510
|
return;
|
|
7417
7511
|
}
|
|
7418
7512
|
const engagementIntervalMinutes = parseInt(
|
|
7419
7513
|
this.state?.TWITTER_ENGAGEMENT_INTERVAL || getSetting(this.runtime, "TWITTER_ENGAGEMENT_INTERVAL") || process.env.TWITTER_ENGAGEMENT_INTERVAL || "30"
|
|
7420
7514
|
);
|
|
7421
7515
|
const actionInterval = engagementIntervalMinutes * 60 * 1e3;
|
|
7422
|
-
|
|
7516
|
+
logger7.info(
|
|
7423
7517
|
`Timeline client will check every ${engagementIntervalMinutes} minutes`
|
|
7424
7518
|
);
|
|
7425
7519
|
this.handleTimeline();
|
|
@@ -7430,7 +7524,7 @@ var TwitterTimelineClient = class {
|
|
|
7430
7524
|
handleTwitterTimelineLoop();
|
|
7431
7525
|
}
|
|
7432
7526
|
async stop() {
|
|
7433
|
-
|
|
7527
|
+
logger7.info("Stopping Twitter timeline client...");
|
|
7434
7528
|
this.isRunning = false;
|
|
7435
7529
|
}
|
|
7436
7530
|
async getTimeline(count) {
|
|
@@ -7439,7 +7533,7 @@ var TwitterTimelineClient = class {
|
|
|
7439
7533
|
return homeTimeline.filter((tweet) => tweet.username !== twitterUsername);
|
|
7440
7534
|
}
|
|
7441
7535
|
createTweetId(runtime, tweet) {
|
|
7442
|
-
return
|
|
7536
|
+
return createUniqueUuid5(runtime, tweet.id);
|
|
7443
7537
|
}
|
|
7444
7538
|
formMessage(runtime, tweet) {
|
|
7445
7539
|
return {
|
|
@@ -7449,33 +7543,32 @@ var TwitterTimelineClient = class {
|
|
|
7449
7543
|
text: tweet.text,
|
|
7450
7544
|
url: tweet.permanentUrl,
|
|
7451
7545
|
imageUrls: tweet.photos?.map((photo) => photo.url) || [],
|
|
7452
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
7546
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid5(runtime, tweet.inReplyToStatusId) : void 0,
|
|
7453
7547
|
source: "twitter",
|
|
7454
|
-
channelType:
|
|
7548
|
+
channelType: ChannelType4.GROUP,
|
|
7455
7549
|
tweet
|
|
7456
7550
|
},
|
|
7457
|
-
entityId:
|
|
7458
|
-
roomId:
|
|
7551
|
+
entityId: createUniqueUuid5(runtime, tweet.userId),
|
|
7552
|
+
roomId: createUniqueUuid5(runtime, tweet.conversationId),
|
|
7459
7553
|
createdAt: getEpochMs(tweet.timestamp)
|
|
7460
7554
|
};
|
|
7461
7555
|
}
|
|
7462
7556
|
async handleTimeline() {
|
|
7463
|
-
|
|
7557
|
+
logger7.info("Starting Twitter timeline processing...");
|
|
7464
7558
|
const tweets = await this.getTimeline(20);
|
|
7465
|
-
|
|
7559
|
+
logger7.info(`Fetched ${tweets.length} tweets from timeline`);
|
|
7466
7560
|
const maxActionsPerCycle = parseInt(
|
|
7467
7561
|
getSetting(this.runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN") || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
|
|
7468
7562
|
);
|
|
7469
7563
|
const tweetDecisions = [];
|
|
7470
7564
|
for (const tweet of tweets) {
|
|
7471
7565
|
try {
|
|
7472
|
-
const
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
logger6.log(`Already processed tweet ID: ${tweet.id}`);
|
|
7566
|
+
const isProcessed = await isTweetProcessed(this.runtime, tweet.id);
|
|
7567
|
+
if (isProcessed) {
|
|
7568
|
+
logger7.log(`Already processed tweet ID: ${tweet.id}`);
|
|
7476
7569
|
continue;
|
|
7477
7570
|
}
|
|
7478
|
-
const roomId =
|
|
7571
|
+
const roomId = createUniqueUuid5(this.runtime, tweet.conversationId);
|
|
7479
7572
|
const message = this.formMessage(this.runtime, tweet);
|
|
7480
7573
|
let state = await this.runtime.composeState(message);
|
|
7481
7574
|
const actionRespondPrompt = composePromptFromState({
|
|
@@ -7496,7 +7589,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
|
|
|
7496
7589
|
);
|
|
7497
7590
|
const parsedResponse = parseActionResponseFromText(actionResponse);
|
|
7498
7591
|
if (!parsedResponse) {
|
|
7499
|
-
|
|
7592
|
+
logger7.debug(`No action response generated for tweet ${tweet.id}`);
|
|
7500
7593
|
continue;
|
|
7501
7594
|
}
|
|
7502
7595
|
tweetDecisions.push({
|
|
@@ -7507,7 +7600,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
|
|
|
7507
7600
|
});
|
|
7508
7601
|
if (tweetDecisions.length >= maxActionsPerCycle) break;
|
|
7509
7602
|
} catch (error) {
|
|
7510
|
-
|
|
7603
|
+
logger7.error(`Error processing tweet ${tweet.id}:`, error);
|
|
7511
7604
|
}
|
|
7512
7605
|
}
|
|
7513
7606
|
const rankByActionRelevance = (arr) => {
|
|
@@ -7525,7 +7618,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
|
|
|
7525
7618
|
});
|
|
7526
7619
|
};
|
|
7527
7620
|
const prioritizedTweets = rankByActionRelevance(tweetDecisions);
|
|
7528
|
-
|
|
7621
|
+
logger7.info(`Processing ${prioritizedTweets.length} tweets with actions`);
|
|
7529
7622
|
if (prioritizedTweets.length > 0) {
|
|
7530
7623
|
const actionSummary = prioritizedTweets.map((td) => {
|
|
7531
7624
|
const actions = [];
|
|
@@ -7535,11 +7628,11 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
|
|
|
7535
7628
|
if (td.actionResponse.reply) actions.push("REPLY");
|
|
7536
7629
|
return `Tweet ${td.tweet.id}: ${actions.join(", ")}`;
|
|
7537
7630
|
});
|
|
7538
|
-
|
|
7631
|
+
logger7.info(`Actions to execute:
|
|
7539
7632
|
${actionSummary.join("\n")}`);
|
|
7540
7633
|
}
|
|
7541
7634
|
await this.processTimelineActions(prioritizedTweets);
|
|
7542
|
-
|
|
7635
|
+
logger7.info("Timeline processing complete");
|
|
7543
7636
|
}
|
|
7544
7637
|
async processTimelineActions(tweetDecisions) {
|
|
7545
7638
|
const results = [];
|
|
@@ -7555,32 +7648,30 @@ ${actionSummary.join("\n")}`);
|
|
|
7555
7648
|
id: roomId,
|
|
7556
7649
|
name: `Twitter conversation ${tweet.conversationId}`,
|
|
7557
7650
|
source: "twitter",
|
|
7558
|
-
type:
|
|
7651
|
+
type: ChannelType4.GROUP,
|
|
7559
7652
|
channelId: tweet.conversationId,
|
|
7560
7653
|
serverId: tweet.userId,
|
|
7561
|
-
worldId:
|
|
7654
|
+
worldId: createUniqueUuid5(this.runtime, tweet.userId)
|
|
7562
7655
|
});
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
tweet
|
|
7573
|
-
},
|
|
7574
|
-
agentId: this.runtime.agentId,
|
|
7575
|
-
roomId,
|
|
7576
|
-
createdAt: getEpochMs(tweet.timestamp)
|
|
7656
|
+
const tweetMemory = {
|
|
7657
|
+
id: tweetId,
|
|
7658
|
+
entityId: createUniqueUuid5(this.runtime, tweet.userId),
|
|
7659
|
+
content: {
|
|
7660
|
+
text: tweet.text,
|
|
7661
|
+
url: tweet.permanentUrl,
|
|
7662
|
+
source: "twitter",
|
|
7663
|
+
channelType: ChannelType4.GROUP,
|
|
7664
|
+
tweet
|
|
7577
7665
|
},
|
|
7578
|
-
|
|
7579
|
-
|
|
7666
|
+
agentId: this.runtime.agentId,
|
|
7667
|
+
roomId,
|
|
7668
|
+
createdAt: getEpochMs(tweet.timestamp)
|
|
7669
|
+
};
|
|
7670
|
+
await createMemorySafe(this.runtime, tweetMemory, "messages");
|
|
7580
7671
|
try {
|
|
7581
7672
|
const userId = tweet.userId;
|
|
7582
|
-
const worldId =
|
|
7583
|
-
const entityId =
|
|
7673
|
+
const worldId = createUniqueUuid5(this.runtime, userId);
|
|
7674
|
+
const entityId = createUniqueUuid5(this.runtime, userId);
|
|
7584
7675
|
await this.ensureTweetWorldContext(tweet, roomId, worldId, entityId);
|
|
7585
7676
|
if (actionResponse.like) {
|
|
7586
7677
|
await this.handleLikeAction(tweet);
|
|
@@ -7600,69 +7691,45 @@ ${actionSummary.join("\n")}`);
|
|
|
7600
7691
|
}
|
|
7601
7692
|
results.push({ tweetId: tweet.id, actionResponse, executedActions });
|
|
7602
7693
|
} catch (error) {
|
|
7603
|
-
|
|
7694
|
+
logger7.error(`Error processing actions for tweet ${tweet.id}:`, error);
|
|
7604
7695
|
}
|
|
7605
7696
|
}
|
|
7606
7697
|
return results;
|
|
7607
7698
|
}
|
|
7608
7699
|
async ensureTweetWorldContext(tweet, roomId, worldId, entityId) {
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
name: tweet.name
|
|
7620
|
-
}
|
|
7621
|
-
}
|
|
7622
|
-
});
|
|
7623
|
-
await this.runtime.ensureConnection({
|
|
7624
|
-
entityId,
|
|
7625
|
-
roomId,
|
|
7626
|
-
userName: tweet.username,
|
|
7627
|
-
name: tweet.name,
|
|
7628
|
-
worldName: `${tweet.name}'s Twitter`,
|
|
7629
|
-
source: "twitter",
|
|
7630
|
-
type: ChannelType3.GROUP,
|
|
7631
|
-
channelId: tweet.conversationId,
|
|
7632
|
-
serverId: tweet.userId,
|
|
7633
|
-
worldId,
|
|
7634
|
-
metadata: {
|
|
7635
|
-
ownership: { ownerId: tweet.userId },
|
|
7636
|
-
twitter: {
|
|
7637
|
-
username: tweet.username,
|
|
7638
|
-
id: tweet.userId,
|
|
7639
|
-
name: tweet.name
|
|
7640
|
-
}
|
|
7641
|
-
}
|
|
7642
|
-
});
|
|
7700
|
+
try {
|
|
7701
|
+
await ensureTwitterContext(this.runtime, {
|
|
7702
|
+
userId: tweet.userId,
|
|
7703
|
+
username: tweet.username,
|
|
7704
|
+
name: tweet.name,
|
|
7705
|
+
conversationId: tweet.conversationId
|
|
7706
|
+
});
|
|
7707
|
+
} catch (error) {
|
|
7708
|
+
logger7.error(`Failed to ensure context for tweet ${tweet.id}:`, error);
|
|
7709
|
+
}
|
|
7643
7710
|
}
|
|
7644
7711
|
async handleLikeAction(tweet) {
|
|
7645
7712
|
try {
|
|
7646
7713
|
if (this.isDryRun) {
|
|
7647
|
-
|
|
7714
|
+
logger7.log(`[DRY RUN] Would have liked tweet ${tweet.id}`);
|
|
7648
7715
|
return;
|
|
7649
7716
|
}
|
|
7650
7717
|
await this.twitterClient.likeTweet(tweet.id);
|
|
7651
|
-
|
|
7718
|
+
logger7.log(`Liked tweet ${tweet.id}`);
|
|
7652
7719
|
} catch (error) {
|
|
7653
|
-
|
|
7720
|
+
logger7.error(`Error liking tweet ${tweet.id}:`, error);
|
|
7654
7721
|
}
|
|
7655
7722
|
}
|
|
7656
7723
|
async handleRetweetAction(tweet) {
|
|
7657
7724
|
try {
|
|
7658
7725
|
if (this.isDryRun) {
|
|
7659
|
-
|
|
7726
|
+
logger7.log(`[DRY RUN] Would have retweeted tweet ${tweet.id}`);
|
|
7660
7727
|
return;
|
|
7661
7728
|
}
|
|
7662
7729
|
await this.twitterClient.retweet(tweet.id);
|
|
7663
|
-
|
|
7730
|
+
logger7.log(`Retweeted tweet ${tweet.id}`);
|
|
7664
7731
|
} catch (error) {
|
|
7665
|
-
|
|
7732
|
+
logger7.error(`Error retweeting tweet ${tweet.id}:`, error);
|
|
7666
7733
|
}
|
|
7667
7734
|
}
|
|
7668
7735
|
async handleQuoteAction(tweet) {
|
|
@@ -7681,7 +7748,7 @@ ${tweet.text}`;
|
|
|
7681
7748
|
const responseObject = parseKeyValueXml(quoteResponse);
|
|
7682
7749
|
if (responseObject.post) {
|
|
7683
7750
|
if (this.isDryRun) {
|
|
7684
|
-
|
|
7751
|
+
logger7.log(
|
|
7685
7752
|
`[DRY RUN] Would have quoted tweet ${tweet.id} with: ${responseObject.post}`
|
|
7686
7753
|
);
|
|
7687
7754
|
return;
|
|
@@ -7695,12 +7762,12 @@ ${tweet.text}`;
|
|
|
7695
7762
|
const body = await result.json();
|
|
7696
7763
|
const tweetResult = body?.data?.create_tweet?.tweet_results?.result || body?.data || body;
|
|
7697
7764
|
if (tweetResult) {
|
|
7698
|
-
|
|
7765
|
+
logger7.log("Successfully posted quote tweet");
|
|
7699
7766
|
} else {
|
|
7700
|
-
|
|
7767
|
+
logger7.error("Quote tweet creation failed:", body);
|
|
7701
7768
|
}
|
|
7702
7769
|
const tweetId = tweetResult?.id || Date.now().toString();
|
|
7703
|
-
const responseId =
|
|
7770
|
+
const responseId = createUniqueUuid5(this.runtime, tweetId);
|
|
7704
7771
|
const responseMemory = {
|
|
7705
7772
|
id: responseId,
|
|
7706
7773
|
entityId: this.runtime.agentId,
|
|
@@ -7712,10 +7779,10 @@ ${tweet.text}`;
|
|
|
7712
7779
|
},
|
|
7713
7780
|
createdAt: Date.now()
|
|
7714
7781
|
};
|
|
7715
|
-
await this.runtime
|
|
7782
|
+
await createMemorySafe(this.runtime, responseMemory, "messages");
|
|
7716
7783
|
}
|
|
7717
7784
|
} catch (error) {
|
|
7718
|
-
|
|
7785
|
+
logger7.error("Error in quote tweet generation:", error);
|
|
7719
7786
|
}
|
|
7720
7787
|
}
|
|
7721
7788
|
async handleReplyAction(tweet) {
|
|
@@ -7734,7 +7801,7 @@ ${tweet.text}`;
|
|
|
7734
7801
|
const responseObject = parseKeyValueXml(replyResponse);
|
|
7735
7802
|
if (responseObject.post) {
|
|
7736
7803
|
if (this.isDryRun) {
|
|
7737
|
-
|
|
7804
|
+
logger7.log(
|
|
7738
7805
|
`[DRY RUN] Would have replied to tweet ${tweet.id} with: ${responseObject.post}`
|
|
7739
7806
|
);
|
|
7740
7807
|
return;
|
|
@@ -7746,8 +7813,8 @@ ${tweet.text}`;
|
|
|
7746
7813
|
tweet.id
|
|
7747
7814
|
);
|
|
7748
7815
|
if (result) {
|
|
7749
|
-
|
|
7750
|
-
const responseId =
|
|
7816
|
+
logger7.log("Successfully posted reply tweet");
|
|
7817
|
+
const responseId = createUniqueUuid5(this.runtime, result.id);
|
|
7751
7818
|
const responseMemory = {
|
|
7752
7819
|
id: responseId,
|
|
7753
7820
|
entityId: this.runtime.agentId,
|
|
@@ -7759,19 +7826,19 @@ ${tweet.text}`;
|
|
|
7759
7826
|
},
|
|
7760
7827
|
createdAt: Date.now()
|
|
7761
7828
|
};
|
|
7762
|
-
await this.runtime
|
|
7829
|
+
await createMemorySafe(this.runtime, responseMemory, "messages");
|
|
7763
7830
|
}
|
|
7764
7831
|
}
|
|
7765
7832
|
} catch (error) {
|
|
7766
|
-
|
|
7833
|
+
logger7.error("Error in reply tweet generation:", error);
|
|
7767
7834
|
}
|
|
7768
7835
|
}
|
|
7769
7836
|
};
|
|
7770
7837
|
|
|
7771
7838
|
// src/discovery.ts
|
|
7772
7839
|
import {
|
|
7773
|
-
createUniqueUuid as
|
|
7774
|
-
logger as
|
|
7840
|
+
createUniqueUuid as createUniqueUuid6,
|
|
7841
|
+
logger as logger8,
|
|
7775
7842
|
ModelType as ModelType4
|
|
7776
7843
|
} from "@elizaos/core";
|
|
7777
7844
|
var TwitterDiscoveryClient = class {
|
|
@@ -7785,7 +7852,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7785
7852
|
const dryRunSetting = state?.TWITTER_DRY_RUN ?? getSetting(this.runtime, "TWITTER_DRY_RUN") ?? process.env.TWITTER_DRY_RUN;
|
|
7786
7853
|
this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
|
|
7787
7854
|
this.config = this.buildDiscoveryConfig();
|
|
7788
|
-
|
|
7855
|
+
logger8.info("Twitter Discovery Config:", {
|
|
7789
7856
|
topics: this.config.topics,
|
|
7790
7857
|
isDryRun: this.isDryRun,
|
|
7791
7858
|
minFollowerCount: this.config.minFollowerCount,
|
|
@@ -7823,7 +7890,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7823
7890
|
topics = this.extractTopicsFromBio(character.bio);
|
|
7824
7891
|
}
|
|
7825
7892
|
} else {
|
|
7826
|
-
|
|
7893
|
+
logger8.warn(
|
|
7827
7894
|
"Character not available in runtime, using default topics for discovery"
|
|
7828
7895
|
);
|
|
7829
7896
|
}
|
|
@@ -7868,21 +7935,21 @@ var TwitterDiscoveryClient = class {
|
|
|
7868
7935
|
return [...new Set(words)].slice(0, 5);
|
|
7869
7936
|
}
|
|
7870
7937
|
async start() {
|
|
7871
|
-
|
|
7938
|
+
logger8.info("Starting Twitter Discovery Client...");
|
|
7872
7939
|
this.isRunning = true;
|
|
7873
7940
|
const discoveryLoop = async () => {
|
|
7874
7941
|
if (!this.isRunning) {
|
|
7875
|
-
|
|
7942
|
+
logger8.info("Discovery client stopped, exiting loop");
|
|
7876
7943
|
return;
|
|
7877
7944
|
}
|
|
7878
7945
|
try {
|
|
7879
7946
|
await this.runDiscoveryCycle();
|
|
7880
7947
|
} catch (error) {
|
|
7881
|
-
|
|
7948
|
+
logger8.error("Discovery cycle error:", error);
|
|
7882
7949
|
}
|
|
7883
7950
|
const discoveryIntervalMinutes = getRandomInterval(this.runtime, "discovery");
|
|
7884
7951
|
const nextInterval = discoveryIntervalMinutes * 60 * 1e3;
|
|
7885
|
-
|
|
7952
|
+
logger8.log(
|
|
7886
7953
|
`Next discovery cycle in ${discoveryIntervalMinutes.toFixed(1)} minutes`
|
|
7887
7954
|
);
|
|
7888
7955
|
setTimeout(discoveryLoop, nextInterval);
|
|
@@ -7890,19 +7957,19 @@ var TwitterDiscoveryClient = class {
|
|
|
7890
7957
|
setTimeout(discoveryLoop, 5e3);
|
|
7891
7958
|
}
|
|
7892
7959
|
async stop() {
|
|
7893
|
-
|
|
7960
|
+
logger8.info("Stopping Twitter Discovery Client...");
|
|
7894
7961
|
this.isRunning = false;
|
|
7895
7962
|
}
|
|
7896
7963
|
async runDiscoveryCycle() {
|
|
7897
|
-
|
|
7964
|
+
logger8.info("Starting Twitter discovery cycle...");
|
|
7898
7965
|
const discoveries = await this.discoverContent();
|
|
7899
7966
|
const { tweets, accounts } = discoveries;
|
|
7900
|
-
|
|
7967
|
+
logger8.info(
|
|
7901
7968
|
`Discovered ${tweets.length} tweets and ${accounts.length} accounts`
|
|
7902
7969
|
);
|
|
7903
7970
|
const followedCount = await this.processAccounts(accounts);
|
|
7904
7971
|
const engagementCount = await this.processTweets(tweets);
|
|
7905
|
-
|
|
7972
|
+
logger8.info(
|
|
7906
7973
|
`Discovery cycle complete: ${followedCount} follows, ${engagementCount} engagements`
|
|
7907
7974
|
);
|
|
7908
7975
|
}
|
|
@@ -7914,7 +7981,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7914
7981
|
allTweets.push(...topicContent.tweets);
|
|
7915
7982
|
topicContent.accounts.forEach((acc) => allAccounts.set(acc.user.id, acc));
|
|
7916
7983
|
} catch (error) {
|
|
7917
|
-
|
|
7984
|
+
logger8.error("Failed to discover from topics:", error);
|
|
7918
7985
|
}
|
|
7919
7986
|
try {
|
|
7920
7987
|
const threadContent = await this.discoverFromThreads();
|
|
@@ -7923,7 +7990,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7923
7990
|
(acc) => allAccounts.set(acc.user.id, acc)
|
|
7924
7991
|
);
|
|
7925
7992
|
} catch (error) {
|
|
7926
|
-
|
|
7993
|
+
logger8.error("Failed to discover from threads:", error);
|
|
7927
7994
|
}
|
|
7928
7995
|
try {
|
|
7929
7996
|
const popularContent = await this.discoverFromPopularAccounts();
|
|
@@ -7932,7 +7999,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7932
7999
|
(acc) => allAccounts.set(acc.user.id, acc)
|
|
7933
8000
|
);
|
|
7934
8001
|
} catch (error) {
|
|
7935
|
-
|
|
8002
|
+
logger8.error("Failed to discover from popular accounts:", error);
|
|
7936
8003
|
}
|
|
7937
8004
|
const sortedTweets = allTweets.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, 50);
|
|
7938
8005
|
const sortedAccounts = Array.from(allAccounts.values()).sort(
|
|
@@ -7941,14 +8008,14 @@ var TwitterDiscoveryClient = class {
|
|
|
7941
8008
|
return { tweets: sortedTweets, accounts: sortedAccounts };
|
|
7942
8009
|
}
|
|
7943
8010
|
async discoverFromTopics() {
|
|
7944
|
-
|
|
8011
|
+
logger8.debug("Discovering from character topics...");
|
|
7945
8012
|
const tweets = [];
|
|
7946
8013
|
const accounts = /* @__PURE__ */ new Map();
|
|
7947
8014
|
for (const topic of this.config.topics.slice(0, 5)) {
|
|
7948
8015
|
try {
|
|
7949
8016
|
const searchTopic = this.sanitizeTopic(topic);
|
|
7950
8017
|
const popularQuery = `${searchTopic} -is:retweet -is:reply lang:en`;
|
|
7951
|
-
|
|
8018
|
+
logger8.debug(`Searching popular tweets for topic: ${topic}`);
|
|
7952
8019
|
const popularResults = await this.twitterClient.fetchSearchTweets(
|
|
7953
8020
|
popularQuery,
|
|
7954
8021
|
20,
|
|
@@ -7977,7 +8044,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7977
8044
|
}
|
|
7978
8045
|
}
|
|
7979
8046
|
const engagedQuery = `${searchTopic} -is:retweet lang:en`;
|
|
7980
|
-
|
|
8047
|
+
logger8.debug(`Searching engaged tweets for topic: ${topic}`);
|
|
7981
8048
|
const engagedResults = await this.twitterClient.fetchSearchTweets(
|
|
7982
8049
|
engagedQuery,
|
|
7983
8050
|
15,
|
|
@@ -8005,19 +8072,19 @@ var TwitterDiscoveryClient = class {
|
|
|
8005
8072
|
}
|
|
8006
8073
|
}
|
|
8007
8074
|
} catch (error) {
|
|
8008
|
-
|
|
8075
|
+
logger8.error(`Failed to search topic ${topic}:`, error);
|
|
8009
8076
|
}
|
|
8010
8077
|
}
|
|
8011
8078
|
return { tweets, accounts: Array.from(accounts.values()) };
|
|
8012
8079
|
}
|
|
8013
8080
|
async discoverFromThreads() {
|
|
8014
|
-
|
|
8081
|
+
logger8.debug("Discovering from conversation threads...");
|
|
8015
8082
|
const tweets = [];
|
|
8016
8083
|
const accounts = /* @__PURE__ */ new Map();
|
|
8017
8084
|
const topicQuery = this.config.topics.slice(0, 3).map((t) => this.sanitizeTopic(t)).join(" OR ");
|
|
8018
8085
|
try {
|
|
8019
8086
|
const viralQuery = `(${topicQuery}) -is:retweet has:mentions`;
|
|
8020
|
-
|
|
8087
|
+
logger8.debug(`Searching viral threads with query: ${viralQuery}`);
|
|
8021
8088
|
const searchResults = await this.twitterClient.fetchSearchTweets(
|
|
8022
8089
|
viralQuery,
|
|
8023
8090
|
15,
|
|
@@ -8040,19 +8107,19 @@ var TwitterDiscoveryClient = class {
|
|
|
8040
8107
|
}
|
|
8041
8108
|
}
|
|
8042
8109
|
} catch (error) {
|
|
8043
|
-
|
|
8110
|
+
logger8.error("Failed to discover threads:", error);
|
|
8044
8111
|
}
|
|
8045
8112
|
return { tweets, accounts: Array.from(accounts.values()) };
|
|
8046
8113
|
}
|
|
8047
8114
|
async discoverFromPopularAccounts() {
|
|
8048
|
-
|
|
8115
|
+
logger8.debug("Discovering from popular accounts in topics...");
|
|
8049
8116
|
const tweets = [];
|
|
8050
8117
|
const accounts = /* @__PURE__ */ new Map();
|
|
8051
8118
|
for (const topic of this.config.topics.slice(0, 3)) {
|
|
8052
8119
|
try {
|
|
8053
8120
|
const searchTopic = this.sanitizeTopic(topic);
|
|
8054
8121
|
const influencerQuery = `${searchTopic} -is:retweet lang:en`;
|
|
8055
|
-
|
|
8122
|
+
logger8.debug(`Searching for influencers in topic: ${topic}`);
|
|
8056
8123
|
const results = await this.twitterClient.fetchSearchTweets(
|
|
8057
8124
|
influencerQuery,
|
|
8058
8125
|
10,
|
|
@@ -8079,7 +8146,7 @@ var TwitterDiscoveryClient = class {
|
|
|
8079
8146
|
}
|
|
8080
8147
|
}
|
|
8081
8148
|
} catch (error) {
|
|
8082
|
-
|
|
8149
|
+
logger8.error(
|
|
8083
8150
|
`Failed to discover popular accounts for ${topic}:`,
|
|
8084
8151
|
error
|
|
8085
8152
|
);
|
|
@@ -8158,13 +8225,13 @@ var TwitterDiscoveryClient = class {
|
|
|
8158
8225
|
for (const scoredAccount of sortedAccounts) {
|
|
8159
8226
|
if (followedCount >= this.config.maxFollowsPerCycle) break;
|
|
8160
8227
|
if (scoredAccount.user.followersCount < this.config.minFollowerCount) {
|
|
8161
|
-
|
|
8228
|
+
logger8.debug(
|
|
8162
8229
|
`Skipping @${scoredAccount.user.username} - below minimum follower count (${scoredAccount.user.followersCount} < ${this.config.minFollowerCount})`
|
|
8163
8230
|
);
|
|
8164
8231
|
continue;
|
|
8165
8232
|
}
|
|
8166
8233
|
if (scoredAccount.qualityScore < 0.2) {
|
|
8167
|
-
|
|
8234
|
+
logger8.debug(
|
|
8168
8235
|
`Skipping @${scoredAccount.user.username} - quality score too low (${scoredAccount.qualityScore.toFixed(2)})`
|
|
8169
8236
|
);
|
|
8170
8237
|
continue;
|
|
@@ -8173,12 +8240,12 @@ var TwitterDiscoveryClient = class {
|
|
|
8173
8240
|
const isFollowing = await this.checkIfFollowing(scoredAccount.user.id);
|
|
8174
8241
|
if (isFollowing) continue;
|
|
8175
8242
|
if (this.isDryRun) {
|
|
8176
|
-
|
|
8243
|
+
logger8.info(
|
|
8177
8244
|
`[DRY RUN] Would follow @${scoredAccount.user.username} (quality: ${scoredAccount.qualityScore.toFixed(2)}, relevance: ${scoredAccount.relevanceScore.toFixed(2)})`
|
|
8178
8245
|
);
|
|
8179
8246
|
} else {
|
|
8180
8247
|
await this.twitterClient.followUser(scoredAccount.user.id);
|
|
8181
|
-
|
|
8248
|
+
logger8.info(
|
|
8182
8249
|
`Followed @${scoredAccount.user.username} (quality: ${scoredAccount.qualityScore.toFixed(2)}, relevance: ${scoredAccount.relevanceScore.toFixed(2)})`
|
|
8183
8250
|
);
|
|
8184
8251
|
await this.saveFollowMemory(scoredAccount.user);
|
|
@@ -8186,7 +8253,7 @@ var TwitterDiscoveryClient = class {
|
|
|
8186
8253
|
followedCount++;
|
|
8187
8254
|
await this.delay(2e3 + Math.random() * 3e3);
|
|
8188
8255
|
} catch (error) {
|
|
8189
|
-
|
|
8256
|
+
logger8.error(
|
|
8190
8257
|
`Failed to follow @${scoredAccount.user.username}:`,
|
|
8191
8258
|
error
|
|
8192
8259
|
);
|
|
@@ -8200,13 +8267,13 @@ var TwitterDiscoveryClient = class {
|
|
|
8200
8267
|
if (engagementCount >= this.config.maxEngagementsPerCycle) break;
|
|
8201
8268
|
if (scoredTweet.engagementType === "skip") continue;
|
|
8202
8269
|
try {
|
|
8203
|
-
const tweetMemoryId =
|
|
8270
|
+
const tweetMemoryId = createUniqueUuid6(
|
|
8204
8271
|
this.runtime,
|
|
8205
8272
|
scoredTweet.tweet.id
|
|
8206
8273
|
);
|
|
8207
8274
|
const existingMemory = await this.runtime.getMemoryById(tweetMemoryId);
|
|
8208
8275
|
if (existingMemory) {
|
|
8209
|
-
|
|
8276
|
+
logger8.debug(
|
|
8210
8277
|
`Already engaged with tweet ${scoredTweet.tweet.id}, skipping`
|
|
8211
8278
|
);
|
|
8212
8279
|
continue;
|
|
@@ -8214,12 +8281,12 @@ var TwitterDiscoveryClient = class {
|
|
|
8214
8281
|
switch (scoredTweet.engagementType) {
|
|
8215
8282
|
case "like":
|
|
8216
8283
|
if (this.isDryRun) {
|
|
8217
|
-
|
|
8284
|
+
logger8.info(
|
|
8218
8285
|
`[DRY RUN] Would like tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
|
|
8219
8286
|
);
|
|
8220
8287
|
} else {
|
|
8221
8288
|
await this.twitterClient.likeTweet(scoredTweet.tweet.id);
|
|
8222
|
-
|
|
8289
|
+
logger8.info(
|
|
8223
8290
|
`Liked tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
|
|
8224
8291
|
);
|
|
8225
8292
|
}
|
|
@@ -8227,7 +8294,7 @@ var TwitterDiscoveryClient = class {
|
|
|
8227
8294
|
case "reply":
|
|
8228
8295
|
const replyText = await this.generateReply(scoredTweet.tweet);
|
|
8229
8296
|
if (this.isDryRun) {
|
|
8230
|
-
|
|
8297
|
+
logger8.info(
|
|
8231
8298
|
`[DRY RUN] Would reply to tweet ${scoredTweet.tweet.id} with: "${replyText}"`
|
|
8232
8299
|
);
|
|
8233
8300
|
} else {
|
|
@@ -8235,13 +8302,13 @@ var TwitterDiscoveryClient = class {
|
|
|
8235
8302
|
replyText,
|
|
8236
8303
|
scoredTweet.tweet.id
|
|
8237
8304
|
);
|
|
8238
|
-
|
|
8305
|
+
logger8.info(`Replied to tweet: ${scoredTweet.tweet.id}`);
|
|
8239
8306
|
}
|
|
8240
8307
|
break;
|
|
8241
8308
|
case "quote":
|
|
8242
8309
|
const quoteText = await this.generateQuote(scoredTweet.tweet);
|
|
8243
8310
|
if (this.isDryRun) {
|
|
8244
|
-
|
|
8311
|
+
logger8.info(
|
|
8245
8312
|
`[DRY RUN] Would quote tweet ${scoredTweet.tweet.id} with: "${quoteText}"`
|
|
8246
8313
|
);
|
|
8247
8314
|
} else {
|
|
@@ -8249,7 +8316,7 @@ var TwitterDiscoveryClient = class {
|
|
|
8249
8316
|
quoteText,
|
|
8250
8317
|
scoredTweet.tweet.id
|
|
8251
8318
|
);
|
|
8252
|
-
|
|
8319
|
+
logger8.info(`Quoted tweet: ${scoredTweet.tweet.id}`);
|
|
8253
8320
|
}
|
|
8254
8321
|
break;
|
|
8255
8322
|
}
|
|
@@ -8261,7 +8328,7 @@ var TwitterDiscoveryClient = class {
|
|
|
8261
8328
|
await this.delay(3e3 + Math.random() * 5e3);
|
|
8262
8329
|
} catch (error) {
|
|
8263
8330
|
if (error?.message?.includes("403")) {
|
|
8264
|
-
|
|
8331
|
+
logger8.warn(
|
|
8265
8332
|
`Permission denied (403) for tweet ${scoredTweet.tweet.id}. This might be a protected account or restricted tweet. Skipping.`
|
|
8266
8333
|
);
|
|
8267
8334
|
await this.saveEngagementMemory(
|
|
@@ -8269,12 +8336,12 @@ var TwitterDiscoveryClient = class {
|
|
|
8269
8336
|
"skip"
|
|
8270
8337
|
);
|
|
8271
8338
|
} else if (error?.message?.includes("429")) {
|
|
8272
|
-
|
|
8339
|
+
logger8.warn(
|
|
8273
8340
|
`Rate limit (429) hit while engaging with tweet ${scoredTweet.tweet.id}. Pausing engagement cycle.`
|
|
8274
8341
|
);
|
|
8275
8342
|
break;
|
|
8276
8343
|
} else {
|
|
8277
|
-
|
|
8344
|
+
logger8.error(
|
|
8278
8345
|
`Failed to engage with tweet ${scoredTweet.tweet.id}:`,
|
|
8279
8346
|
error
|
|
8280
8347
|
);
|
|
@@ -8360,12 +8427,65 @@ Quote tweet:`;
|
|
|
8360
8427
|
return response.trim();
|
|
8361
8428
|
}
|
|
8362
8429
|
async saveEngagementMemory(tweet, engagementType) {
|
|
8363
|
-
|
|
8364
|
-
|
|
8430
|
+
try {
|
|
8431
|
+
const context = await ensureTwitterContext(this.runtime, {
|
|
8432
|
+
userId: tweet.userId,
|
|
8433
|
+
username: tweet.username,
|
|
8434
|
+
conversationId: tweet.conversationId || tweet.id
|
|
8435
|
+
});
|
|
8436
|
+
const memory = {
|
|
8437
|
+
id: createUniqueUuid6(this.runtime, `${tweet.id}-${engagementType}`),
|
|
8438
|
+
entityId: context.entityId,
|
|
8439
|
+
content: {
|
|
8440
|
+
text: `${engagementType} tweet from @${tweet.username}: ${tweet.text}`,
|
|
8441
|
+
metadata: {
|
|
8442
|
+
tweetId: tweet.id,
|
|
8443
|
+
engagementType,
|
|
8444
|
+
source: "discovery",
|
|
8445
|
+
isDryRun: this.isDryRun
|
|
8446
|
+
}
|
|
8447
|
+
},
|
|
8448
|
+
roomId: context.roomId,
|
|
8449
|
+
agentId: this.runtime.agentId,
|
|
8450
|
+
createdAt: Date.now()
|
|
8451
|
+
};
|
|
8452
|
+
await createMemorySafe(this.runtime, memory, "messages");
|
|
8453
|
+
logger8.debug(`[Discovery] Saved ${engagementType} memory for tweet ${tweet.id}`);
|
|
8454
|
+
} catch (error) {
|
|
8455
|
+
logger8.error(`[Discovery] Failed to save engagement memory:`, error);
|
|
8456
|
+
}
|
|
8365
8457
|
}
|
|
8366
8458
|
async saveFollowMemory(user) {
|
|
8367
|
-
|
|
8368
|
-
|
|
8459
|
+
try {
|
|
8460
|
+
const context = await ensureTwitterContext(this.runtime, {
|
|
8461
|
+
userId: user.id,
|
|
8462
|
+
username: user.username,
|
|
8463
|
+
name: user.name,
|
|
8464
|
+
conversationId: `twitter-follows`
|
|
8465
|
+
});
|
|
8466
|
+
const memory = {
|
|
8467
|
+
id: createUniqueUuid6(this.runtime, `follow-${user.id}`),
|
|
8468
|
+
entityId: context.entityId,
|
|
8469
|
+
content: {
|
|
8470
|
+
text: `followed twitter user ${user.id} @${user.username}`,
|
|
8471
|
+
metadata: {
|
|
8472
|
+
userId: user.id,
|
|
8473
|
+
username: user.username,
|
|
8474
|
+
name: user.name,
|
|
8475
|
+
followersCount: user.followersCount,
|
|
8476
|
+
source: "discovery",
|
|
8477
|
+
isDryRun: this.isDryRun
|
|
8478
|
+
}
|
|
8479
|
+
},
|
|
8480
|
+
roomId: context.roomId,
|
|
8481
|
+
agentId: this.runtime.agentId,
|
|
8482
|
+
createdAt: Date.now()
|
|
8483
|
+
};
|
|
8484
|
+
await createMemorySafe(this.runtime, memory, "messages");
|
|
8485
|
+
logger8.debug(`[Discovery] Saved follow memory for @${user.username}`);
|
|
8486
|
+
} catch (error) {
|
|
8487
|
+
logger8.error(`[Discovery] Failed to save follow memory:`, error);
|
|
8488
|
+
}
|
|
8369
8489
|
}
|
|
8370
8490
|
delay(ms) {
|
|
8371
8491
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -8374,9 +8494,9 @@ Quote tweet:`;
|
|
|
8374
8494
|
|
|
8375
8495
|
// src/base.ts
|
|
8376
8496
|
import {
|
|
8377
|
-
ChannelType as
|
|
8378
|
-
createUniqueUuid as
|
|
8379
|
-
logger as
|
|
8497
|
+
ChannelType as ChannelType5,
|
|
8498
|
+
createUniqueUuid as createUniqueUuid7,
|
|
8499
|
+
logger as logger9
|
|
8380
8500
|
} from "@elizaos/core";
|
|
8381
8501
|
var RequestQueue = class {
|
|
8382
8502
|
constructor() {
|
|
@@ -8421,7 +8541,7 @@ var RequestQueue = class {
|
|
|
8421
8541
|
await request();
|
|
8422
8542
|
this.retryAttempts.delete(request);
|
|
8423
8543
|
} catch (error) {
|
|
8424
|
-
|
|
8544
|
+
logger9.error("Error processing request:", error);
|
|
8425
8545
|
const retryCount = (this.retryAttempts.get(request) || 0) + 1;
|
|
8426
8546
|
if (retryCount < this.maxRetries) {
|
|
8427
8547
|
this.retryAttempts.set(request, retryCount);
|
|
@@ -8429,7 +8549,7 @@ var RequestQueue = class {
|
|
|
8429
8549
|
await this.exponentialBackoff(retryCount);
|
|
8430
8550
|
break;
|
|
8431
8551
|
} else {
|
|
8432
|
-
|
|
8552
|
+
logger9.error(
|
|
8433
8553
|
`Max retries (${this.maxRetries}) exceeded for request, skipping`
|
|
8434
8554
|
);
|
|
8435
8555
|
this.retryAttempts.delete(request);
|
|
@@ -8487,7 +8607,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8487
8607
|
*/
|
|
8488
8608
|
async cacheTweet(tweet) {
|
|
8489
8609
|
if (!tweet) {
|
|
8490
|
-
|
|
8610
|
+
logger9.warn("Tweet is undefined, skipping cache");
|
|
8491
8611
|
return;
|
|
8492
8612
|
}
|
|
8493
8613
|
this.runtime.setCache(`twitter/tweets/${tweet.id}`, tweet);
|
|
@@ -8552,7 +8672,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8552
8672
|
let lastError = null;
|
|
8553
8673
|
while (retryCount < maxRetries) {
|
|
8554
8674
|
try {
|
|
8555
|
-
|
|
8675
|
+
logger9.log("Initializing Twitter API v2 client");
|
|
8556
8676
|
await this.twitterClient.login(
|
|
8557
8677
|
"",
|
|
8558
8678
|
// username not needed for API v2
|
|
@@ -8568,18 +8688,18 @@ var _ClientBase = class _ClientBase {
|
|
|
8568
8688
|
accessTokenSecret
|
|
8569
8689
|
);
|
|
8570
8690
|
if (await this.twitterClient.isLoggedIn()) {
|
|
8571
|
-
|
|
8691
|
+
logger9.info("Successfully authenticated with Twitter API v2");
|
|
8572
8692
|
break;
|
|
8573
8693
|
}
|
|
8574
8694
|
} catch (error) {
|
|
8575
8695
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
8576
|
-
|
|
8696
|
+
logger9.error(
|
|
8577
8697
|
`Authentication attempt ${retryCount + 1} failed: ${lastError.message}`
|
|
8578
8698
|
);
|
|
8579
8699
|
retryCount++;
|
|
8580
8700
|
if (retryCount < maxRetries) {
|
|
8581
8701
|
const delay = 2 ** retryCount * 1e3;
|
|
8582
|
-
|
|
8702
|
+
logger9.info(`Retrying in ${delay / 1e3} seconds...`);
|
|
8583
8703
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
8584
8704
|
}
|
|
8585
8705
|
}
|
|
@@ -8591,13 +8711,13 @@ var _ClientBase = class _ClientBase {
|
|
|
8591
8711
|
}
|
|
8592
8712
|
const profile = await this.twitterClient.me();
|
|
8593
8713
|
if (profile) {
|
|
8594
|
-
|
|
8595
|
-
|
|
8714
|
+
logger9.log("Twitter user ID:", profile.userId);
|
|
8715
|
+
logger9.log("Twitter loaded:", JSON.stringify(profile, null, 10));
|
|
8596
8716
|
const agentId = this.runtime.agentId;
|
|
8597
8717
|
const entity = await this.runtime.getEntityById(agentId);
|
|
8598
8718
|
const entityMetadata = entity?.metadata;
|
|
8599
8719
|
if (entityMetadata?.twitter?.userName !== profile.username) {
|
|
8600
|
-
|
|
8720
|
+
logger9.log(
|
|
8601
8721
|
"Updating Agents known X/twitter handle",
|
|
8602
8722
|
profile.username,
|
|
8603
8723
|
"was",
|
|
@@ -8636,7 +8756,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8636
8756
|
await this.populateTimeline();
|
|
8637
8757
|
}
|
|
8638
8758
|
async fetchOwnPosts(count) {
|
|
8639
|
-
|
|
8759
|
+
logger9.debug("fetching own posts");
|
|
8640
8760
|
const homeTimeline = await this.twitterClient.getUserTweets(
|
|
8641
8761
|
this.profile.id,
|
|
8642
8762
|
count
|
|
@@ -8647,7 +8767,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8647
8767
|
* Fetch timeline for twitter account, optionally only from followed accounts
|
|
8648
8768
|
*/
|
|
8649
8769
|
async fetchHomeTimeline(count, following) {
|
|
8650
|
-
|
|
8770
|
+
logger9.debug("fetching home timeline");
|
|
8651
8771
|
const homeTimeline = following ? await this.twitterClient.fetchFollowingTimeline(count, []) : await this.twitterClient.fetchHomeTimeline(count, []);
|
|
8652
8772
|
return homeTimeline;
|
|
8653
8773
|
}
|
|
@@ -8670,40 +8790,40 @@ var _ClientBase = class _ClientBase {
|
|
|
8670
8790
|
);
|
|
8671
8791
|
return result ?? { tweets: [] };
|
|
8672
8792
|
} catch (error) {
|
|
8673
|
-
|
|
8793
|
+
logger9.error("Error fetching search tweets:", error);
|
|
8674
8794
|
return { tweets: [] };
|
|
8675
8795
|
}
|
|
8676
8796
|
} catch (error) {
|
|
8677
|
-
|
|
8797
|
+
logger9.error("Error fetching search tweets:", error);
|
|
8678
8798
|
return { tweets: [] };
|
|
8679
8799
|
}
|
|
8680
8800
|
}
|
|
8681
8801
|
async populateTimeline() {
|
|
8682
|
-
|
|
8802
|
+
logger9.debug("populating timeline...");
|
|
8683
8803
|
const cachedTimeline = await this.getCachedTimeline();
|
|
8684
8804
|
if (cachedTimeline) {
|
|
8685
8805
|
const existingMemories2 = await this.runtime.getMemoriesByRoomIds({
|
|
8686
8806
|
tableName: "messages",
|
|
8687
8807
|
roomIds: cachedTimeline.map(
|
|
8688
|
-
(tweet) =>
|
|
8808
|
+
(tweet) => createUniqueUuid7(this.runtime, tweet.conversationId)
|
|
8689
8809
|
)
|
|
8690
8810
|
});
|
|
8691
8811
|
const existingMemoryIds2 = new Set(
|
|
8692
8812
|
existingMemories2.map((memory) => memory.id.toString())
|
|
8693
8813
|
);
|
|
8694
8814
|
const someCachedTweetsExist = cachedTimeline.some(
|
|
8695
|
-
(tweet) => existingMemoryIds2.has(
|
|
8815
|
+
(tweet) => existingMemoryIds2.has(createUniqueUuid7(this.runtime, tweet.id))
|
|
8696
8816
|
);
|
|
8697
8817
|
if (someCachedTweetsExist) {
|
|
8698
8818
|
const tweetsToSave2 = cachedTimeline.filter(
|
|
8699
|
-
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(
|
|
8819
|
+
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(createUniqueUuid7(this.runtime, tweet.id))
|
|
8700
8820
|
);
|
|
8701
8821
|
for (const tweet of tweetsToSave2) {
|
|
8702
|
-
|
|
8822
|
+
logger9.log("Saving Tweet", tweet.id);
|
|
8703
8823
|
if (tweet.userId === this.profile.id) {
|
|
8704
8824
|
continue;
|
|
8705
8825
|
}
|
|
8706
|
-
const worldId =
|
|
8826
|
+
const worldId = createUniqueUuid7(this.runtime, tweet.userId);
|
|
8707
8827
|
await this.runtime.ensureWorldExists({
|
|
8708
8828
|
id: worldId,
|
|
8709
8829
|
name: `${tweet.username}'s Twitter`,
|
|
@@ -8717,26 +8837,26 @@ var _ClientBase = class _ClientBase {
|
|
|
8717
8837
|
}
|
|
8718
8838
|
}
|
|
8719
8839
|
});
|
|
8720
|
-
const roomId =
|
|
8721
|
-
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId :
|
|
8840
|
+
const roomId = createUniqueUuid7(this.runtime, tweet.conversationId);
|
|
8841
|
+
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId : createUniqueUuid7(this.runtime, tweet.userId);
|
|
8722
8842
|
await this.runtime.ensureConnection({
|
|
8723
8843
|
entityId,
|
|
8724
8844
|
roomId,
|
|
8725
8845
|
userName: tweet.username,
|
|
8726
8846
|
name: tweet.name,
|
|
8727
8847
|
source: "twitter",
|
|
8728
|
-
type:
|
|
8848
|
+
type: ChannelType5.FEED,
|
|
8729
8849
|
worldId
|
|
8730
8850
|
});
|
|
8731
8851
|
const content = {
|
|
8732
8852
|
text: tweet.text,
|
|
8733
8853
|
url: tweet.permanentUrl,
|
|
8734
8854
|
source: "twitter",
|
|
8735
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
8855
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid7(this.runtime, tweet.inReplyToStatusId) : void 0
|
|
8736
8856
|
};
|
|
8737
8857
|
await this.runtime.createMemory(
|
|
8738
8858
|
{
|
|
8739
|
-
id:
|
|
8859
|
+
id: createUniqueUuid7(this.runtime, tweet.id),
|
|
8740
8860
|
entityId,
|
|
8741
8861
|
content,
|
|
8742
8862
|
agentId: this.runtime.agentId,
|
|
@@ -8747,7 +8867,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8747
8867
|
);
|
|
8748
8868
|
await this.cacheTweet(tweet);
|
|
8749
8869
|
}
|
|
8750
|
-
|
|
8870
|
+
logger9.log(
|
|
8751
8871
|
`Populated ${tweetsToSave2.length} missing tweets from the cache.`
|
|
8752
8872
|
);
|
|
8753
8873
|
return;
|
|
@@ -8764,7 +8884,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8764
8884
|
const roomIds = /* @__PURE__ */ new Set();
|
|
8765
8885
|
for (const tweet of allTweets) {
|
|
8766
8886
|
tweetIdsToCheck.add(tweet.id);
|
|
8767
|
-
roomIds.add(
|
|
8887
|
+
roomIds.add(createUniqueUuid7(this.runtime, tweet.conversationId));
|
|
8768
8888
|
}
|
|
8769
8889
|
const existingMemories = await this.runtime.getMemoriesByRoomIds({
|
|
8770
8890
|
tableName: "messages",
|
|
@@ -8774,17 +8894,17 @@ var _ClientBase = class _ClientBase {
|
|
|
8774
8894
|
existingMemories.map((memory) => memory.id)
|
|
8775
8895
|
);
|
|
8776
8896
|
const tweetsToSave = allTweets.filter(
|
|
8777
|
-
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(
|
|
8897
|
+
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(createUniqueUuid7(this.runtime, tweet.id))
|
|
8778
8898
|
);
|
|
8779
|
-
|
|
8899
|
+
logger9.debug({
|
|
8780
8900
|
processingTweets: tweetsToSave.map((tweet) => tweet.id).join(",")
|
|
8781
8901
|
});
|
|
8782
8902
|
for (const tweet of tweetsToSave) {
|
|
8783
|
-
|
|
8903
|
+
logger9.log("Saving Tweet", tweet.id);
|
|
8784
8904
|
if (tweet.userId === this.profile.id) {
|
|
8785
8905
|
continue;
|
|
8786
8906
|
}
|
|
8787
|
-
const worldId =
|
|
8907
|
+
const worldId = createUniqueUuid7(this.runtime, tweet.userId);
|
|
8788
8908
|
await this.runtime.ensureWorldExists({
|
|
8789
8909
|
id: worldId,
|
|
8790
8910
|
name: `${tweet.username}'s Twitter`,
|
|
@@ -8798,26 +8918,27 @@ var _ClientBase = class _ClientBase {
|
|
|
8798
8918
|
}
|
|
8799
8919
|
}
|
|
8800
8920
|
});
|
|
8801
|
-
const roomId =
|
|
8802
|
-
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId :
|
|
8921
|
+
const roomId = createUniqueUuid7(this.runtime, tweet.conversationId);
|
|
8922
|
+
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId : createUniqueUuid7(this.runtime, tweet.userId);
|
|
8803
8923
|
await this.runtime.ensureConnection({
|
|
8804
8924
|
entityId,
|
|
8805
8925
|
roomId,
|
|
8806
8926
|
userName: tweet.username,
|
|
8807
8927
|
name: tweet.name,
|
|
8808
8928
|
source: "twitter",
|
|
8809
|
-
type:
|
|
8929
|
+
type: ChannelType5.FEED,
|
|
8810
8930
|
worldId
|
|
8811
8931
|
});
|
|
8812
8932
|
const content = {
|
|
8813
8933
|
text: tweet.text,
|
|
8814
8934
|
url: tweet.permanentUrl,
|
|
8815
8935
|
source: "twitter",
|
|
8816
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
8936
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid7(this.runtime, tweet.inReplyToStatusId) : void 0
|
|
8817
8937
|
};
|
|
8818
|
-
await
|
|
8938
|
+
await createMemorySafe(
|
|
8939
|
+
this.runtime,
|
|
8819
8940
|
{
|
|
8820
|
-
id:
|
|
8941
|
+
id: createUniqueUuid7(this.runtime, tweet.id),
|
|
8821
8942
|
entityId,
|
|
8822
8943
|
content,
|
|
8823
8944
|
agentId: this.runtime.agentId,
|
|
@@ -8840,9 +8961,9 @@ var _ClientBase = class _ClientBase {
|
|
|
8840
8961
|
unique: false
|
|
8841
8962
|
});
|
|
8842
8963
|
if (recentMessage.length > 0 && recentMessage[0].content === message.content) {
|
|
8843
|
-
|
|
8964
|
+
logger9.debug("Message already saved", recentMessage[0].id);
|
|
8844
8965
|
} else {
|
|
8845
|
-
await this.runtime
|
|
8966
|
+
await createMemorySafe(this.runtime, message, "messages");
|
|
8846
8967
|
}
|
|
8847
8968
|
await this.runtime.evaluate(message, {
|
|
8848
8969
|
...state,
|
|
@@ -8913,7 +9034,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8913
9034
|
});
|
|
8914
9035
|
return profile;
|
|
8915
9036
|
} catch (error) {
|
|
8916
|
-
|
|
9037
|
+
logger9.error("Error fetching Twitter profile:", error);
|
|
8917
9038
|
throw error;
|
|
8918
9039
|
}
|
|
8919
9040
|
}
|
|
@@ -8934,7 +9055,7 @@ var _ClientBase = class _ClientBase {
|
|
|
8934
9055
|
(tweet) => this.formatTweetToInteraction(tweet)
|
|
8935
9056
|
);
|
|
8936
9057
|
} catch (error) {
|
|
8937
|
-
|
|
9058
|
+
logger9.error("Error fetching Twitter interactions:", error);
|
|
8938
9059
|
return [];
|
|
8939
9060
|
}
|
|
8940
9061
|
}
|
|
@@ -8964,42 +9085,42 @@ var TwitterClientInstance = class {
|
|
|
8964
9085
|
constructor(runtime, state) {
|
|
8965
9086
|
this.client = new ClientBase(runtime, state);
|
|
8966
9087
|
const postEnabledSetting = getSetting(runtime, "TWITTER_ENABLE_POST") ?? process.env.TWITTER_ENABLE_POST;
|
|
8967
|
-
|
|
9088
|
+
logger10.debug(
|
|
8968
9089
|
`TWITTER_ENABLE_POST setting value: ${JSON.stringify(postEnabledSetting)}, type: ${typeof postEnabledSetting}`
|
|
8969
9090
|
);
|
|
8970
|
-
const postEnabled = postEnabledSetting === "true" || postEnabledSetting === true;
|
|
9091
|
+
const postEnabled = typeof postEnabledSetting === "string" && (postEnabledSetting === "true" || postEnabledSetting.toLowerCase() === "true") || typeof postEnabledSetting === "boolean" && postEnabledSetting === true;
|
|
8971
9092
|
if (postEnabled) {
|
|
8972
|
-
|
|
9093
|
+
logger10.info("Twitter posting is ENABLED - creating post client");
|
|
8973
9094
|
this.post = new TwitterPostClient(this.client, runtime, state);
|
|
8974
9095
|
} else {
|
|
8975
|
-
|
|
9096
|
+
logger10.info(
|
|
8976
9097
|
"Twitter posting is DISABLED - set TWITTER_ENABLE_POST=true to enable automatic posting"
|
|
8977
9098
|
);
|
|
8978
9099
|
}
|
|
8979
9100
|
const repliesEnabled = (getSetting(runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
|
|
8980
9101
|
if (repliesEnabled) {
|
|
8981
|
-
|
|
9102
|
+
logger10.info("Twitter replies/interactions are ENABLED");
|
|
8982
9103
|
this.interaction = new TwitterInteractionClient(
|
|
8983
9104
|
this.client,
|
|
8984
9105
|
runtime,
|
|
8985
9106
|
state
|
|
8986
9107
|
);
|
|
8987
9108
|
} else {
|
|
8988
|
-
|
|
9109
|
+
logger10.info("Twitter replies/interactions are DISABLED");
|
|
8989
9110
|
}
|
|
8990
9111
|
const actionsEnabled = (getSetting(runtime, "TWITTER_ENABLE_ACTIONS") ?? process.env.TWITTER_ENABLE_ACTIONS) === "true";
|
|
8991
9112
|
if (actionsEnabled) {
|
|
8992
|
-
|
|
9113
|
+
logger10.info("Twitter timeline actions are ENABLED");
|
|
8993
9114
|
this.timeline = new TwitterTimelineClient(this.client, runtime, state);
|
|
8994
9115
|
} else {
|
|
8995
|
-
|
|
9116
|
+
logger10.info("Twitter timeline actions are DISABLED");
|
|
8996
9117
|
}
|
|
8997
9118
|
const discoveryEnabled = (getSetting(runtime, "TWITTER_ENABLE_DISCOVERY") ?? process.env.TWITTER_ENABLE_DISCOVERY) === "true" || actionsEnabled && (getSetting(runtime, "TWITTER_ENABLE_DISCOVERY") ?? process.env.TWITTER_ENABLE_DISCOVERY) !== "false";
|
|
8998
9119
|
if (discoveryEnabled) {
|
|
8999
|
-
|
|
9120
|
+
logger10.info("Twitter discovery service is ENABLED");
|
|
9000
9121
|
this.discovery = new TwitterDiscoveryClient(this.client, runtime, state);
|
|
9001
9122
|
} else {
|
|
9002
|
-
|
|
9123
|
+
logger10.info(
|
|
9003
9124
|
"Twitter discovery service is DISABLED - set TWITTER_ENABLE_DISCOVERY=true to enable"
|
|
9004
9125
|
);
|
|
9005
9126
|
}
|
|
@@ -9016,28 +9137,28 @@ var _TwitterService = class _TwitterService extends Service {
|
|
|
9016
9137
|
service.runtime = runtime;
|
|
9017
9138
|
try {
|
|
9018
9139
|
await validateTwitterConfig(runtime);
|
|
9019
|
-
|
|
9140
|
+
logger10.log("\u2705 Twitter configuration validated successfully");
|
|
9020
9141
|
service.twitterClient = new TwitterClientInstance(runtime, {});
|
|
9021
9142
|
await service.twitterClient.client.init();
|
|
9022
9143
|
if (service.twitterClient.post) {
|
|
9023
|
-
|
|
9144
|
+
logger10.log("\u{1F4EE} Starting Twitter post client...");
|
|
9024
9145
|
await service.twitterClient.post.start();
|
|
9025
9146
|
}
|
|
9026
9147
|
if (service.twitterClient.interaction) {
|
|
9027
|
-
|
|
9148
|
+
logger10.log("\u{1F4AC} Starting Twitter interaction client...");
|
|
9028
9149
|
await service.twitterClient.interaction.start();
|
|
9029
9150
|
}
|
|
9030
9151
|
if (service.twitterClient.timeline) {
|
|
9031
|
-
|
|
9152
|
+
logger10.log("\u{1F4CA} Starting Twitter timeline client...");
|
|
9032
9153
|
await service.twitterClient.timeline.start();
|
|
9033
9154
|
}
|
|
9034
9155
|
if (service.twitterClient.discovery) {
|
|
9035
|
-
|
|
9156
|
+
logger10.log("\u{1F50D} Starting Twitter discovery client...");
|
|
9036
9157
|
await service.twitterClient.discovery.start();
|
|
9037
9158
|
}
|
|
9038
|
-
|
|
9159
|
+
logger10.log("\u2705 Twitter service started successfully");
|
|
9039
9160
|
} catch (error) {
|
|
9040
|
-
|
|
9161
|
+
logger10.error("\u{1F6A8} Failed to start Twitter service:", error);
|
|
9041
9162
|
throw error;
|
|
9042
9163
|
}
|
|
9043
9164
|
return service;
|
|
@@ -9055,7 +9176,7 @@ var _TwitterService = class _TwitterService extends Service {
|
|
|
9055
9176
|
if (this.twitterClient?.discovery) {
|
|
9056
9177
|
await this.twitterClient.discovery.stop();
|
|
9057
9178
|
}
|
|
9058
|
-
|
|
9179
|
+
logger10.log("Twitter service stopped");
|
|
9059
9180
|
}
|
|
9060
9181
|
};
|
|
9061
9182
|
_TwitterService.serviceType = "twitter";
|
|
@@ -9063,7 +9184,7 @@ var TwitterService = _TwitterService;
|
|
|
9063
9184
|
|
|
9064
9185
|
// src/actions/postTweet.ts
|
|
9065
9186
|
import {
|
|
9066
|
-
logger as
|
|
9187
|
+
logger as logger11,
|
|
9067
9188
|
ModelType as ModelType5
|
|
9068
9189
|
} from "@elizaos/core";
|
|
9069
9190
|
var postTweetAction = {
|
|
@@ -9076,34 +9197,12 @@ var postTweetAction = {
|
|
|
9076
9197
|
"SHARE_ON_TWITTER"
|
|
9077
9198
|
],
|
|
9078
9199
|
validate: async (runtime, message) => {
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
hasContent: !!message.content,
|
|
9082
|
-
contentType: message.content?.type,
|
|
9083
|
-
hasText: !!message.content?.text,
|
|
9084
|
-
textLength: message.content?.text?.length || 0,
|
|
9085
|
-
source: message.content?.source,
|
|
9086
|
-
roomId: message.roomId,
|
|
9087
|
-
entityId: message.entityId
|
|
9088
|
-
});
|
|
9089
|
-
if (message.content?.type === "post" && !message.content?.text) {
|
|
9090
|
-
logger10.debug("Skipping validation for provider context (empty post type)");
|
|
9091
|
-
return false;
|
|
9092
|
-
}
|
|
9093
|
-
const text = message.content?.text?.trim();
|
|
9094
|
-
if (!text || text.length === 0) {
|
|
9095
|
-
logger10.error("No text content for tweet");
|
|
9096
|
-
logger10.debug("Stack trace:", new Error().stack);
|
|
9097
|
-
return false;
|
|
9098
|
-
}
|
|
9099
|
-
if (text.length > 280) {
|
|
9100
|
-
logger10.warn(`Tweet too long: ${text.length} characters`);
|
|
9101
|
-
}
|
|
9102
|
-
return true;
|
|
9200
|
+
const service = runtime.getService("twitter");
|
|
9201
|
+
return !!service;
|
|
9103
9202
|
},
|
|
9104
9203
|
description: "Post a tweet on Twitter",
|
|
9105
9204
|
handler: async (runtime, message, state, _options, callback) => {
|
|
9106
|
-
|
|
9205
|
+
logger11.info("Executing POST_TWEET action");
|
|
9107
9206
|
try {
|
|
9108
9207
|
const twitterService = runtime.getService("twitter");
|
|
9109
9208
|
if (!twitterService) {
|
|
@@ -9121,7 +9220,7 @@ var postTweetAction = {
|
|
|
9121
9220
|
}
|
|
9122
9221
|
let text = message.content?.text?.trim();
|
|
9123
9222
|
if (!text) {
|
|
9124
|
-
|
|
9223
|
+
logger11.error("No text content for tweet");
|
|
9125
9224
|
if (callback) {
|
|
9126
9225
|
callback({
|
|
9127
9226
|
text: "I need something to tweet! Please provide the text.",
|
|
@@ -9131,7 +9230,7 @@ var postTweetAction = {
|
|
|
9131
9230
|
return;
|
|
9132
9231
|
}
|
|
9133
9232
|
if (text.length > 280) {
|
|
9134
|
-
|
|
9233
|
+
logger11.info(`Truncating tweet from ${text.length} to 280 characters`);
|
|
9135
9234
|
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
|
|
9136
9235
|
let truncated = "";
|
|
9137
9236
|
for (const sentence of sentences) {
|
|
@@ -9142,7 +9241,7 @@ var postTweetAction = {
|
|
|
9142
9241
|
}
|
|
9143
9242
|
}
|
|
9144
9243
|
text = truncated.trim() || text.substring(0, 277) + "...";
|
|
9145
|
-
|
|
9244
|
+
logger11.info(`Truncated tweet: ${text}`);
|
|
9146
9245
|
}
|
|
9147
9246
|
let finalTweetText = text;
|
|
9148
9247
|
if (text.length < 50 || text.toLowerCase().includes("post") || text.toLowerCase().includes("tweet")) {
|
|
@@ -9193,20 +9292,24 @@ Tweet:`;
|
|
|
9193
9292
|
tweetId = Date.now().toString();
|
|
9194
9293
|
}
|
|
9195
9294
|
const tweetUrl = `https://twitter.com/${client.profile.username}/status/${tweetId}`;
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9295
|
+
logger11.info(`Successfully posted tweet: ${tweetId}`);
|
|
9296
|
+
try {
|
|
9297
|
+
await runtime.createMemory(
|
|
9298
|
+
{
|
|
9299
|
+
entityId: runtime.agentId,
|
|
9300
|
+
content: {
|
|
9301
|
+
text: finalTweetText,
|
|
9302
|
+
url: tweetUrl,
|
|
9303
|
+
source: "twitter",
|
|
9304
|
+
action: "POST_TWEET"
|
|
9305
|
+
},
|
|
9306
|
+
roomId: message.roomId
|
|
9205
9307
|
},
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9308
|
+
"messages"
|
|
9309
|
+
);
|
|
9310
|
+
} catch (memoryError) {
|
|
9311
|
+
logger11.error("Failed to create memory for posted tweet:", memoryError);
|
|
9312
|
+
}
|
|
9210
9313
|
if (callback) {
|
|
9211
9314
|
await callback({
|
|
9212
9315
|
text: `I've posted a tweet: "${finalTweetText}"
|
|
@@ -9223,7 +9326,7 @@ View it here: ${tweetUrl}`,
|
|
|
9223
9326
|
throw new Error("Failed to post tweet - no response data");
|
|
9224
9327
|
}
|
|
9225
9328
|
} catch (error) {
|
|
9226
|
-
|
|
9329
|
+
logger11.error("Error posting tweet:", error);
|
|
9227
9330
|
if (callback) {
|
|
9228
9331
|
await callback({
|
|
9229
9332
|
text: `Sorry, I couldn't post the tweet. Error: ${error.message}`,
|
|
@@ -9238,28 +9341,13 @@ View it here: ${tweetUrl}`,
|
|
|
9238
9341
|
{
|
|
9239
9342
|
name: "{{user1}}",
|
|
9240
9343
|
content: {
|
|
9241
|
-
text: "Post a tweet about the
|
|
9242
|
-
}
|
|
9243
|
-
},
|
|
9244
|
-
{
|
|
9245
|
-
name: "{{agentName}}",
|
|
9246
|
-
content: {
|
|
9247
|
-
text: "I'll post a tweet about open source AI for you.",
|
|
9248
|
-
action: "POST_TWEET"
|
|
9249
|
-
}
|
|
9250
|
-
}
|
|
9251
|
-
],
|
|
9252
|
-
[
|
|
9253
|
-
{
|
|
9254
|
-
name: "{{user1}}",
|
|
9255
|
-
content: {
|
|
9256
|
-
text: "Tweet something interesting about web3"
|
|
9344
|
+
text: "Post a tweet about the weather today"
|
|
9257
9345
|
}
|
|
9258
9346
|
},
|
|
9259
9347
|
{
|
|
9260
|
-
name: "{{
|
|
9348
|
+
name: "{{agent}}",
|
|
9261
9349
|
content: {
|
|
9262
|
-
text: "I'll
|
|
9350
|
+
text: "I'll post a tweet about today's weather for you.",
|
|
9263
9351
|
action: "POST_TWEET"
|
|
9264
9352
|
}
|
|
9265
9353
|
}
|
|
@@ -9268,13 +9356,13 @@ View it here: ${tweetUrl}`,
|
|
|
9268
9356
|
{
|
|
9269
9357
|
name: "{{user1}}",
|
|
9270
9358
|
content: {
|
|
9271
|
-
text: "
|
|
9359
|
+
text: "Tweet: The future of AI is collaborative intelligence"
|
|
9272
9360
|
}
|
|
9273
9361
|
},
|
|
9274
9362
|
{
|
|
9275
|
-
name: "{{
|
|
9363
|
+
name: "{{agent}}",
|
|
9276
9364
|
content: {
|
|
9277
|
-
text: "I'll post
|
|
9365
|
+
text: "I'll post that tweet for you.",
|
|
9278
9366
|
action: "POST_TWEET"
|
|
9279
9367
|
}
|
|
9280
9368
|
}
|
|
@@ -9289,7 +9377,7 @@ var TwitterPlugin = {
|
|
|
9289
9377
|
actions: [postTweetAction],
|
|
9290
9378
|
services: [TwitterService],
|
|
9291
9379
|
init: async (_config, runtime) => {
|
|
9292
|
-
|
|
9380
|
+
logger12.log("\u{1F527} Initializing Twitter plugin...");
|
|
9293
9381
|
const hasGetSetting = runtime && typeof runtime.getSetting === "function";
|
|
9294
9382
|
const apiKey = hasGetSetting ? runtime.getSetting("TWITTER_API_KEY") : process.env.TWITTER_API_KEY;
|
|
9295
9383
|
const apiSecretKey = hasGetSetting ? runtime.getSetting("TWITTER_API_SECRET_KEY") : process.env.TWITTER_API_SECRET_KEY;
|
|
@@ -9301,14 +9389,14 @@ var TwitterPlugin = {
|
|
|
9301
9389
|
if (!apiSecretKey) missing.push("TWITTER_API_SECRET_KEY");
|
|
9302
9390
|
if (!accessToken) missing.push("TWITTER_ACCESS_TOKEN");
|
|
9303
9391
|
if (!accessTokenSecret) missing.push("TWITTER_ACCESS_TOKEN_SECRET");
|
|
9304
|
-
|
|
9392
|
+
logger12.warn(
|
|
9305
9393
|
`Twitter API credentials not configured - Twitter functionality will be limited. Missing: ${missing.join(", ")}`
|
|
9306
9394
|
);
|
|
9307
|
-
|
|
9395
|
+
logger12.warn(
|
|
9308
9396
|
"To enable Twitter functionality, please provide the missing credentials in your .env file"
|
|
9309
9397
|
);
|
|
9310
9398
|
} else {
|
|
9311
|
-
|
|
9399
|
+
logger12.log("\u2705 Twitter credentials found");
|
|
9312
9400
|
}
|
|
9313
9401
|
}
|
|
9314
9402
|
};
|