@elizaos/plugin-twitter 1.2.19 → 1.2.21

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