@elizaos/plugin-twitter 1.2.20 → 1.2.21

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