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