@elizaos/plugin-twitter 1.2.13 → 1.2.15

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,10 +5,10 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/index.ts
8
- import { logger as logger10 } from "@elizaos/core";
8
+ import { logger as logger11 } from "@elizaos/core";
9
9
 
10
10
  // src/services/twitter.service.ts
11
- import { Service, logger as logger8 } from "@elizaos/core";
11
+ import { Service, logger as logger9 } from "@elizaos/core";
12
12
 
13
13
  // src/interactions.ts
14
14
  import {
@@ -16,7 +16,7 @@ import {
16
16
  EventType,
17
17
  ModelType,
18
18
  createUniqueUuid as createUniqueUuid2,
19
- logger as logger3
19
+ logger as logger4
20
20
  } from "@elizaos/core";
21
21
 
22
22
  // src/client/auth.ts
@@ -2014,6 +2014,9 @@ var parseActionResponseFromText = (text) => {
2014
2014
  return { actions };
2015
2015
  };
2016
2016
 
2017
+ // src/environment.ts
2018
+ import { logger as logger3 } from "@elizaos/core";
2019
+
2017
2020
  // src/utils/settings.ts
2018
2021
  function getSetting(runtime, key, defaultValue) {
2019
2022
  if (runtime && typeof runtime.getSetting === "function") {
@@ -6079,11 +6082,24 @@ var twitterEnvSchema = external_exports.object({
6079
6082
  // likes, retweets, quotes
6080
6083
  // Timing configuration (all in minutes)
6081
6084
  TWITTER_POST_INTERVAL: external_exports.string().default("120"),
6082
- // minutes between posts
6085
+ // minutes between posts (deprecated, kept for backwards compatibility)
6086
+ TWITTER_POST_INTERVAL_MIN: external_exports.string().default("90"),
6087
+ // minimum minutes between posts
6088
+ TWITTER_POST_INTERVAL_MAX: external_exports.string().default("150"),
6089
+ // maximum minutes between posts
6083
6090
  TWITTER_ENGAGEMENT_INTERVAL: external_exports.string().default("30"),
6084
- // minutes between all interactions
6091
+ // minutes between all interactions (deprecated, kept for backwards compatibility)
6092
+ TWITTER_ENGAGEMENT_INTERVAL_MIN: external_exports.string().default("20"),
6093
+ // minimum minutes between engagements
6094
+ TWITTER_ENGAGEMENT_INTERVAL_MAX: external_exports.string().default("40"),
6095
+ // maximum minutes between engagements
6096
+ TWITTER_DISCOVERY_INTERVAL_MIN: external_exports.string().default("15"),
6097
+ // minimum minutes between discovery cycles
6098
+ TWITTER_DISCOVERY_INTERVAL_MAX: external_exports.string().default("30"),
6099
+ // maximum minutes between discovery cycles
6085
6100
  // Limits
6086
- TWITTER_MAX_ENGAGEMENTS_PER_RUN: external_exports.string().default("10"),
6101
+ TWITTER_MAX_ENGAGEMENTS_PER_RUN: external_exports.string().default("5"),
6102
+ // Reduced from 10 to be less aggressive
6087
6103
  TWITTER_MAX_TWEET_LENGTH: external_exports.string().default("280"),
6088
6104
  // standard tweet length
6089
6105
  // Advanced
@@ -6145,16 +6161,52 @@ async function validateTwitterConfig(runtime, config = {}) {
6145
6161
  120
6146
6162
  )
6147
6163
  ),
6164
+ TWITTER_POST_INTERVAL_MIN: String(
6165
+ safeParseInt(
6166
+ config.TWITTER_POST_INTERVAL_MIN ?? getSetting(runtime, "TWITTER_POST_INTERVAL_MIN"),
6167
+ 90
6168
+ )
6169
+ ),
6170
+ TWITTER_POST_INTERVAL_MAX: String(
6171
+ safeParseInt(
6172
+ config.TWITTER_POST_INTERVAL_MAX ?? getSetting(runtime, "TWITTER_POST_INTERVAL_MAX"),
6173
+ 150
6174
+ )
6175
+ ),
6148
6176
  TWITTER_ENGAGEMENT_INTERVAL: String(
6149
6177
  safeParseInt(
6150
6178
  config.TWITTER_ENGAGEMENT_INTERVAL ?? getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL"),
6151
6179
  30
6152
6180
  )
6153
6181
  ),
6182
+ TWITTER_ENGAGEMENT_INTERVAL_MIN: String(
6183
+ safeParseInt(
6184
+ config.TWITTER_ENGAGEMENT_INTERVAL_MIN ?? getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL_MIN"),
6185
+ 20
6186
+ )
6187
+ ),
6188
+ TWITTER_ENGAGEMENT_INTERVAL_MAX: String(
6189
+ safeParseInt(
6190
+ config.TWITTER_ENGAGEMENT_INTERVAL_MAX ?? getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL_MAX"),
6191
+ 40
6192
+ )
6193
+ ),
6194
+ TWITTER_DISCOVERY_INTERVAL_MIN: String(
6195
+ safeParseInt(
6196
+ config.TWITTER_DISCOVERY_INTERVAL_MIN ?? getSetting(runtime, "TWITTER_DISCOVERY_INTERVAL_MIN"),
6197
+ 15
6198
+ )
6199
+ ),
6200
+ TWITTER_DISCOVERY_INTERVAL_MAX: String(
6201
+ safeParseInt(
6202
+ config.TWITTER_DISCOVERY_INTERVAL_MAX ?? getSetting(runtime, "TWITTER_DISCOVERY_INTERVAL_MAX"),
6203
+ 30
6204
+ )
6205
+ ),
6154
6206
  TWITTER_MAX_ENGAGEMENTS_PER_RUN: String(
6155
6207
  safeParseInt(
6156
6208
  config.TWITTER_MAX_ENGAGEMENTS_PER_RUN ?? getSetting(runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN"),
6157
- 10
6209
+ 5
6158
6210
  )
6159
6211
  ),
6160
6212
  TWITTER_MAX_TWEET_LENGTH: String(
@@ -6186,6 +6238,49 @@ async function validateTwitterConfig(runtime, config = {}) {
6186
6238
  throw error;
6187
6239
  }
6188
6240
  }
6241
+ function getRandomInterval(runtime, type) {
6242
+ let minInterval;
6243
+ let maxInterval;
6244
+ let fallbackInterval;
6245
+ switch (type) {
6246
+ case "post":
6247
+ const postMin = getSetting(runtime, "TWITTER_POST_INTERVAL_MIN");
6248
+ const postMax = getSetting(runtime, "TWITTER_POST_INTERVAL_MAX");
6249
+ minInterval = postMin ? safeParseInt(postMin, 0) : void 0;
6250
+ maxInterval = postMax ? safeParseInt(postMax, 0) : void 0;
6251
+ fallbackInterval = safeParseInt(
6252
+ getSetting(runtime, "TWITTER_POST_INTERVAL"),
6253
+ 120
6254
+ );
6255
+ break;
6256
+ case "engagement":
6257
+ const engagementMin = getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL_MIN");
6258
+ const engagementMax = getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL_MAX");
6259
+ minInterval = engagementMin ? safeParseInt(engagementMin, 0) : void 0;
6260
+ maxInterval = engagementMax ? safeParseInt(engagementMax, 0) : void 0;
6261
+ fallbackInterval = safeParseInt(
6262
+ getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL"),
6263
+ 30
6264
+ );
6265
+ break;
6266
+ case "discovery":
6267
+ const discoveryMin = getSetting(runtime, "TWITTER_DISCOVERY_INTERVAL_MIN");
6268
+ const discoveryMax = getSetting(runtime, "TWITTER_DISCOVERY_INTERVAL_MAX");
6269
+ minInterval = discoveryMin ? safeParseInt(discoveryMin, 0) : void 0;
6270
+ maxInterval = discoveryMax ? safeParseInt(discoveryMax, 0) : void 0;
6271
+ fallbackInterval = 20;
6272
+ break;
6273
+ default:
6274
+ throw new Error(`Unknown interval type: ${type}`);
6275
+ }
6276
+ if (minInterval !== void 0 && maxInterval !== void 0 && minInterval < maxInterval) {
6277
+ const randomInterval = Math.random() * (maxInterval - minInterval) + minInterval;
6278
+ logger3.debug(`Random ${type} interval: ${randomInterval.toFixed(1)} minutes (between ${minInterval}-${maxInterval})`);
6279
+ return randomInterval;
6280
+ }
6281
+ logger3.debug(`Using fixed ${type} interval: ${fallbackInterval} minutes`);
6282
+ return fallbackInterval;
6283
+ }
6189
6284
 
6190
6285
  // src/interactions.ts
6191
6286
  var TwitterInteractionClient = class {
@@ -6213,15 +6308,13 @@ var TwitterInteractionClient = class {
6213
6308
  this.isRunning = true;
6214
6309
  const handleTwitterInteractionsLoop = () => {
6215
6310
  if (!this.isRunning) {
6216
- logger3.info("Twitter interaction client stopped, exiting loop");
6311
+ logger4.info("Twitter interaction client stopped, exiting loop");
6217
6312
  return;
6218
6313
  }
6219
- const engagementIntervalMinutes = parseInt(
6220
- this.state?.TWITTER_ENGAGEMENT_INTERVAL || getSetting(this.runtime, "TWITTER_ENGAGEMENT_INTERVAL") || process.env.TWITTER_ENGAGEMENT_INTERVAL || "30"
6221
- );
6314
+ const engagementIntervalMinutes = getRandomInterval(this.runtime, "engagement");
6222
6315
  const interactionInterval = engagementIntervalMinutes * 60 * 1e3;
6223
- logger3.info(
6224
- `Twitter interaction client will check every ${engagementIntervalMinutes} minutes`
6316
+ logger4.info(
6317
+ `Twitter interaction client will check in ${engagementIntervalMinutes.toFixed(1)} minutes`
6225
6318
  );
6226
6319
  this.handleTwitterInteractions();
6227
6320
  if (this.isRunning) {
@@ -6234,14 +6327,14 @@ var TwitterInteractionClient = class {
6234
6327
  * Stops the Twitter interaction client
6235
6328
  */
6236
6329
  async stop() {
6237
- logger3.log("Stopping Twitter interaction client...");
6330
+ logger4.log("Stopping Twitter interaction client...");
6238
6331
  this.isRunning = false;
6239
6332
  }
6240
6333
  /**
6241
6334
  * Asynchronously handles Twitter interactions by checking for mentions and target user posts.
6242
6335
  */
6243
6336
  async handleTwitterInteractions() {
6244
- logger3.log("Checking Twitter interactions");
6337
+ logger4.log("Checking Twitter interactions");
6245
6338
  const twitterUsername = this.client.profile?.username;
6246
6339
  try {
6247
6340
  const repliesEnabled = (getSetting(this.runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
@@ -6253,9 +6346,9 @@ var TwitterInteractionClient = class {
6253
6346
  await this.handleTargetUserPosts(targetUsersConfig);
6254
6347
  }
6255
6348
  await this.client.cacheLatestCheckedTweetId();
6256
- logger3.log("Finished checking Twitter interactions");
6349
+ logger4.log("Finished checking Twitter interactions");
6257
6350
  } catch (error) {
6258
- logger3.error("Error handling Twitter interactions:", error);
6351
+ logger4.error("Error handling Twitter interactions:", error);
6259
6352
  }
6260
6353
  }
6261
6354
  /**
@@ -6279,7 +6372,7 @@ var TwitterInteractionClient = class {
6279
6372
  }
6280
6373
  await this.processMentionTweets(mentionCandidates);
6281
6374
  } catch (error) {
6282
- logger3.error("Error handling mentions:", error);
6375
+ logger4.error("Error handling mentions:", error);
6283
6376
  }
6284
6377
  }
6285
6378
  /**
@@ -6291,7 +6384,7 @@ var TwitterInteractionClient = class {
6291
6384
  if (targetUsers.length === 0 && !targetUsersConfig.includes("*")) {
6292
6385
  return;
6293
6386
  }
6294
- logger3.info(
6387
+ logger4.info(
6295
6388
  `Checking posts from target users: ${targetUsers.join(", ") || "everyone (*)"}`
6296
6389
  );
6297
6390
  for (const targetUser of targetUsers) {
@@ -6305,7 +6398,7 @@ var TwitterInteractionClient = class {
6305
6398
  1 /* Latest */
6306
6399
  );
6307
6400
  if (searchResult.tweets.length > 0) {
6308
- logger3.info(
6401
+ logger4.info(
6309
6402
  `Found ${searchResult.tweets.length} posts from @${normalizedUsername}`
6310
6403
  );
6311
6404
  await this.processTargetUserTweets(
@@ -6314,14 +6407,14 @@ var TwitterInteractionClient = class {
6314
6407
  );
6315
6408
  }
6316
6409
  } catch (error) {
6317
- logger3.error(`Error searching posts from @${targetUser}:`, error);
6410
+ logger4.error(`Error searching posts from @${targetUser}:`, error);
6318
6411
  }
6319
6412
  }
6320
6413
  if (targetUsersConfig.includes("*")) {
6321
6414
  await this.processTimelineForEngagement();
6322
6415
  }
6323
6416
  } catch (error) {
6324
- logger3.error("Error handling target user posts:", error);
6417
+ logger4.error("Error handling target user posts:", error);
6325
6418
  }
6326
6419
  }
6327
6420
  /**
@@ -6334,7 +6427,7 @@ var TwitterInteractionClient = class {
6334
6427
  let engagementCount = 0;
6335
6428
  for (const tweet of tweets) {
6336
6429
  if (engagementCount >= maxEngagementsPerRun) {
6337
- logger3.info(`Reached max engagements limit (${maxEngagementsPerRun})`);
6430
+ logger4.info(`Reached max engagements limit (${maxEngagementsPerRun})`);
6338
6431
  break;
6339
6432
  }
6340
6433
  const tweetId = createUniqueUuid2(this.runtime, tweet.id);
@@ -6349,7 +6442,7 @@ var TwitterInteractionClient = class {
6349
6442
  }
6350
6443
  const shouldEngage = await this.shouldEngageWithTweet(tweet);
6351
6444
  if (shouldEngage) {
6352
- logger3.info(
6445
+ logger4.info(
6353
6446
  `Engaging with tweet from @${username}: ${tweet.text.substring(0, 50)}...`
6354
6447
  );
6355
6448
  await this.ensureTweetContext(tweet);
@@ -6375,13 +6468,13 @@ var TwitterInteractionClient = class {
6375
6468
  return tweetAge < 12 * 60 * 60 * 1e3;
6376
6469
  });
6377
6470
  if (relevantTweets.length > 0) {
6378
- logger3.info(
6471
+ logger4.info(
6379
6472
  `Found ${relevantTweets.length} relevant tweets from timeline`
6380
6473
  );
6381
6474
  await this.processTargetUserTweets(relevantTweets, "timeline");
6382
6475
  }
6383
6476
  } catch (error) {
6384
- logger3.error("Error processing timeline for engagement:", error);
6477
+ logger4.error("Error processing timeline for engagement:", error);
6385
6478
  }
6386
6479
  }
6387
6480
  /**
@@ -6434,7 +6527,7 @@ Response (YES/NO):`;
6434
6527
  });
6435
6528
  return response.trim().toUpperCase().includes("YES");
6436
6529
  } catch (error) {
6437
- logger3.error("Error determining engagement:", error);
6530
+ logger4.error("Error determining engagement:", error);
6438
6531
  return false;
6439
6532
  }
6440
6533
  }
@@ -6460,6 +6553,15 @@ Response (YES/NO):`;
6460
6553
  }
6461
6554
  });
6462
6555
  const roomId = createUniqueUuid2(this.runtime, conversationId);
6556
+ await this.runtime.ensureRoomExists({
6557
+ id: roomId,
6558
+ name: `Twitter conversation ${conversationId}`,
6559
+ source: "twitter",
6560
+ type: ChannelType.FEED,
6561
+ channelId: conversationId,
6562
+ serverId: userId,
6563
+ worldId
6564
+ });
6463
6565
  const entityId = createUniqueUuid2(this.runtime, userId);
6464
6566
  await this.runtime.ensureConnection({
6465
6567
  entityId,
@@ -6509,7 +6611,7 @@ Response (YES/NO):`;
6509
6611
  });
6510
6612
  return result.text && result.text.length > 0;
6511
6613
  } catch (error) {
6512
- logger3.error("Error engaging with tweet:", error);
6614
+ logger4.error("Error engaging with tweet:", error);
6513
6615
  return false;
6514
6616
  }
6515
6617
  }
@@ -6524,7 +6626,7 @@ Response (YES/NO):`;
6524
6626
  * Note: MENTION_RECEIVED is currently disabled (see TODO below)
6525
6627
  */
6526
6628
  async processMentionTweets(mentionCandidates) {
6527
- logger3.log(
6629
+ logger4.log(
6528
6630
  "Completed checking mentioned tweets:",
6529
6631
  mentionCandidates.length
6530
6632
  );
@@ -6538,7 +6640,7 @@ Response (YES/NO):`;
6538
6640
  targetUsersConfig
6539
6641
  );
6540
6642
  if (!shouldTarget) {
6541
- logger3.log(
6643
+ logger4.log(
6542
6644
  `Skipping tweet from @${tweet.username} - not in target users list`
6543
6645
  );
6544
6646
  }
@@ -6552,7 +6654,7 @@ Response (YES/NO):`;
6552
6654
  0,
6553
6655
  maxInteractionsPerRun
6554
6656
  );
6555
- logger3.info(
6657
+ logger4.info(
6556
6658
  `Processing ${tweetsToProcess.length} of ${uniqueTweetCandidates.length} mention tweets (max: ${maxInteractionsPerRun})`
6557
6659
  );
6558
6660
  for (const tweet of tweetsToProcess) {
@@ -6560,7 +6662,7 @@ Response (YES/NO):`;
6560
6662
  const tweetId = createUniqueUuid2(this.runtime, tweet.id);
6561
6663
  const existingResponse = await this.runtime.getMemoryById(tweetId);
6562
6664
  if (existingResponse) {
6563
- logger3.log(`Already responded to tweet ${tweet.id}, skipping`);
6665
+ logger4.log(`Already responded to tweet ${tweet.id}, skipping`);
6564
6666
  continue;
6565
6667
  }
6566
6668
  const conversationRoomId = createUniqueUuid2(
@@ -6577,22 +6679,22 @@ Response (YES/NO):`;
6577
6679
  (memory2) => memory2.content.inReplyTo === tweetId || memory2.content.inReplyTo === tweet.id
6578
6680
  );
6579
6681
  if (hasExistingReply) {
6580
- logger3.log(
6682
+ logger4.log(
6581
6683
  `Already responded to tweet ${tweet.id} (found in conversation history), skipping`
6582
6684
  );
6583
6685
  continue;
6584
6686
  }
6585
- logger3.log("New Tweet found", tweet.id);
6687
+ logger4.log("New Tweet found", tweet.id);
6586
6688
  const userId = tweet.userId;
6587
6689
  const conversationId = tweet.conversationId || tweet.id;
6588
6690
  const roomId = createUniqueUuid2(this.runtime, conversationId);
6589
6691
  const username = tweet.username;
6590
- logger3.log("----");
6591
- logger3.log(`User: ${username} (${userId})`);
6592
- logger3.log(`Tweet: ${tweet.id}`);
6593
- logger3.log(`Conversation: ${conversationId}`);
6594
- logger3.log(`Room: ${roomId}`);
6595
- logger3.log("----");
6692
+ logger4.log("----");
6693
+ logger4.log(`User: ${username} (${userId})`);
6694
+ logger4.log(`Tweet: ${tweet.id}`);
6695
+ logger4.log(`Conversation: ${conversationId}`);
6696
+ logger4.log(`Room: ${roomId}`);
6697
+ logger4.log("----");
6596
6698
  const worldId = createUniqueUuid2(this.runtime, userId);
6597
6699
  await this.runtime.ensureWorldExists({
6598
6700
  id: worldId,
@@ -6617,6 +6719,15 @@ Response (YES/NO):`;
6617
6719
  type: ChannelType.FEED,
6618
6720
  worldId
6619
6721
  });
6722
+ await this.runtime.ensureRoomExists({
6723
+ id: roomId,
6724
+ name: `Twitter conversation ${conversationId}`,
6725
+ source: "twitter",
6726
+ type: ChannelType.FEED,
6727
+ channelId: conversationId,
6728
+ serverId: userId,
6729
+ worldId
6730
+ });
6620
6731
  const memory = {
6621
6732
  id: tweetId,
6622
6733
  entityId,
@@ -6630,7 +6741,7 @@ Response (YES/NO):`;
6630
6741
  roomId,
6631
6742
  createdAt: tweet.timestamp * 1e3
6632
6743
  };
6633
- logger3.log("Saving tweet memory...");
6744
+ logger4.log("Saving tweet memory...");
6634
6745
  await this.runtime.createMemory(memory, "messages");
6635
6746
  if (tweet.thread && tweet.thread.length > 0) {
6636
6747
  const threadStartId = tweet.thread[0].id;
@@ -6813,23 +6924,23 @@ Response (YES/NO):`;
6813
6924
  thread
6814
6925
  }) {
6815
6926
  if (!message.content.text) {
6816
- logger3.log("Skipping Tweet with no text", tweet.id);
6927
+ logger4.log("Skipping Tweet with no text", tweet.id);
6817
6928
  return { text: "", actions: ["IGNORE"] };
6818
6929
  }
6819
6930
  const callback = async (response2, tweetId) => {
6820
6931
  try {
6821
6932
  if (!response2.text) {
6822
- logger3.warn("No text content in response, skipping tweet reply");
6933
+ logger4.warn("No text content in response, skipping tweet reply");
6823
6934
  return [];
6824
6935
  }
6825
6936
  const tweetToReplyTo = tweetId || tweet.id;
6826
6937
  if (this.isDryRun) {
6827
- logger3.info(
6938
+ logger4.info(
6828
6939
  `[DRY RUN] Would have replied to ${tweet.username} with: ${response2.text}`
6829
6940
  );
6830
6941
  return [];
6831
6942
  }
6832
- logger3.info(`Replying to tweet ${tweetToReplyTo}`);
6943
+ logger4.info(`Replying to tweet ${tweetToReplyTo}`);
6833
6944
  const tweetResult = await sendTweet(
6834
6945
  this.client,
6835
6946
  response2.text,
@@ -6855,7 +6966,7 @@ Response (YES/NO):`;
6855
6966
  await this.runtime.createMemory(responseMemory, "messages");
6856
6967
  return [responseMemory];
6857
6968
  } catch (error) {
6858
- logger3.error("Error in tweet reply callback:", error);
6969
+ logger4.error("Error in tweet reply callback:", error);
6859
6970
  return [];
6860
6971
  }
6861
6972
  };
@@ -6894,8 +7005,9 @@ Response (YES/NO):`;
6894
7005
 
6895
7006
  // src/post.ts
6896
7007
  import {
7008
+ ChannelType as ChannelType2,
6897
7009
  createUniqueUuid as createUniqueUuid3,
6898
- logger as logger4,
7010
+ logger as logger5,
6899
7011
  ModelType as ModelType2
6900
7012
  } from "@elizaos/core";
6901
7013
  var TwitterPostClient = class {
@@ -6914,39 +7026,40 @@ var TwitterPostClient = class {
6914
7026
  this.runtime = runtime;
6915
7027
  const dryRunSetting = this.state?.TWITTER_DRY_RUN ?? getSetting(this.runtime, "TWITTER_DRY_RUN") ?? process.env.TWITTER_DRY_RUN;
6916
7028
  this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
6917
- logger4.log("Twitter Post Client Configuration:");
6918
- logger4.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
6919
- const postIntervalMinutes = parseInt(
6920
- this.state?.TWITTER_POST_INTERVAL || getSetting(this.runtime, "TWITTER_POST_INTERVAL") || process.env.TWITTER_POST_INTERVAL || "120"
7029
+ logger5.log("Twitter Post Client Configuration:");
7030
+ logger5.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
7031
+ const postIntervalMin = parseInt(
7032
+ this.state?.TWITTER_POST_INTERVAL_MIN || getSetting(this.runtime, "TWITTER_POST_INTERVAL_MIN") || process.env.TWITTER_POST_INTERVAL_MIN || "90"
7033
+ );
7034
+ const postIntervalMax = parseInt(
7035
+ this.state?.TWITTER_POST_INTERVAL_MAX || getSetting(this.runtime, "TWITTER_POST_INTERVAL_MAX") || process.env.TWITTER_POST_INTERVAL_MAX || "150"
6921
7036
  );
6922
- logger4.log(`- Post Interval: ${postIntervalMinutes} minutes`);
7037
+ logger5.log(`- Post Interval: ${postIntervalMin}-${postIntervalMax} minutes (randomized)`);
6923
7038
  }
6924
7039
  /**
6925
7040
  * Stops the Twitter post client
6926
7041
  */
6927
7042
  async stop() {
6928
- logger4.log("Stopping Twitter post client...");
7043
+ logger5.log("Stopping Twitter post client...");
6929
7044
  this.isRunning = false;
6930
7045
  }
6931
7046
  /**
6932
7047
  * Starts the Twitter post client, setting up a loop to periodically generate new tweets.
6933
7048
  */
6934
7049
  async start() {
6935
- logger4.log("Starting Twitter post client...");
7050
+ logger5.log("Starting Twitter post client...");
6936
7051
  this.isRunning = true;
6937
7052
  const generateNewTweetLoop = async () => {
6938
7053
  if (!this.isRunning) {
6939
- logger4.log("Twitter post client stopped, exiting loop");
7054
+ logger5.log("Twitter post client stopped, exiting loop");
6940
7055
  return;
6941
7056
  }
6942
- const postIntervalMinutes = parseInt(
6943
- this.state?.TWITTER_POST_INTERVAL || getSetting(this.runtime, "TWITTER_POST_INTERVAL") || process.env.TWITTER_POST_INTERVAL || "120"
6944
- );
7057
+ const postIntervalMinutes = getRandomInterval(this.runtime, "post");
6945
7058
  const interval = postIntervalMinutes * 60 * 1e3;
6946
- logger4.info(`Next tweet scheduled in ${postIntervalMinutes} minutes`);
7059
+ logger5.info(`Next tweet scheduled in ${postIntervalMinutes.toFixed(1)} minutes`);
6947
7060
  await new Promise((resolve) => setTimeout(resolve, interval));
6948
7061
  if (!this.isRunning) {
6949
- logger4.log("Twitter post client stopped during wait, exiting loop");
7062
+ logger5.log("Twitter post client stopped during wait, exiting loop");
6950
7063
  return;
6951
7064
  }
6952
7065
  await this.generateNewTweet();
@@ -6957,7 +7070,7 @@ var TwitterPostClient = class {
6957
7070
  await new Promise((resolve) => setTimeout(resolve, 1e3));
6958
7071
  const postImmediately = this.state?.TWITTER_POST_IMMEDIATELY || getSetting(this.runtime, "TWITTER_POST_IMMEDIATELY") || process.env.TWITTER_POST_IMMEDIATELY;
6959
7072
  if (postImmediately === "true" || postImmediately === true) {
6960
- logger4.info(
7073
+ logger5.info(
6961
7074
  "TWITTER_POST_IMMEDIATELY is true, generating initial tweet now"
6962
7075
  );
6963
7076
  await this.generateNewTweet();
@@ -6969,19 +7082,19 @@ var TwitterPostClient = class {
6969
7082
  * This approach aligns with our platform-independent architecture.
6970
7083
  */
6971
7084
  async generateNewTweet() {
6972
- logger4.info("Attempting to generate new tweet...");
7085
+ logger5.info("Attempting to generate new tweet...");
6973
7086
  if (this.isPosting) {
6974
- logger4.info("Already posting a tweet, skipping concurrent attempt");
7087
+ logger5.info("Already posting a tweet, skipping concurrent attempt");
6975
7088
  return;
6976
7089
  }
6977
7090
  this.isPosting = true;
6978
7091
  try {
6979
7092
  const userId = this.client.profile?.id;
6980
7093
  if (!userId) {
6981
- logger4.error("Cannot generate tweet: Twitter profile not available");
7094
+ logger5.error("Cannot generate tweet: Twitter profile not available");
6982
7095
  return;
6983
7096
  }
6984
- logger4.info(
7097
+ logger5.info(
6985
7098
  `Generating tweet for user: ${this.client.profile?.username} (${userId})`
6986
7099
  );
6987
7100
  const worldId = createUniqueUuid3(this.runtime, userId);
@@ -6993,7 +7106,7 @@ var TwitterPostClient = class {
6993
7106
  content: { text: "", type: "post" },
6994
7107
  createdAt: Date.now()
6995
7108
  }).catch((error) => {
6996
- logger4.warn("Error composing state, using minimal state:", error);
7109
+ logger5.warn("Error composing state, using minimal state:", error);
6997
7110
  return {
6998
7111
  agentId: this.runtime.agentId,
6999
7112
  recentMemories: [],
@@ -7041,15 +7154,15 @@ Generate a single tweet that sounds like YOU would actually write it:`;
7041
7154
  );
7042
7155
  const tweetText = generatedContent.trim();
7043
7156
  if (!tweetText || tweetText.length === 0) {
7044
- logger4.error("Generated empty tweet content");
7157
+ logger5.error("Generated empty tweet content");
7045
7158
  return;
7046
7159
  }
7047
7160
  if (tweetText.includes("Error: Missing")) {
7048
- logger4.error("Error in generated content:", tweetText);
7161
+ logger5.error("Error in generated content:", tweetText);
7049
7162
  return;
7050
7163
  }
7051
7164
  if (tweetText.length > 280) {
7052
- logger4.warn(`Generated tweet too long (${tweetText.length} chars), truncating...`);
7165
+ logger5.warn(`Generated tweet too long (${tweetText.length} chars), truncating...`);
7053
7166
  const sentences = tweetText.match(/[^.!?]+[.!?]+/g) || [tweetText];
7054
7167
  let truncated = "";
7055
7168
  for (const sentence of sentences) {
@@ -7060,39 +7173,72 @@ Generate a single tweet that sounds like YOU would actually write it:`;
7060
7173
  }
7061
7174
  }
7062
7175
  const finalTweet = truncated.trim() || tweetText.substring(0, 277) + "...";
7063
- logger4.info(`Truncated tweet: ${finalTweet}`);
7176
+ logger5.info(`Truncated tweet: ${finalTweet}`);
7064
7177
  if (this.isDryRun) {
7065
- logger4.info(`[DRY RUN] Would post tweet: ${finalTweet}`);
7178
+ logger5.info(`[DRY RUN] Would post tweet: ${finalTweet}`);
7066
7179
  return;
7067
7180
  }
7068
7181
  const result2 = await this.postToTwitter(finalTweet, []);
7069
7182
  if (result2 === null) {
7070
- logger4.info("Skipped posting duplicate tweet");
7183
+ logger5.info("Skipped posting duplicate tweet");
7071
7184
  return;
7072
7185
  }
7073
7186
  const tweetId2 = result2.id;
7074
- logger4.info(`Tweet posted successfully! ID: ${tweetId2}`);
7075
- logger4.info("Tweet posted successfully (memory saving disabled due to room constraints)");
7187
+ logger5.info(`Tweet posted successfully! ID: ${tweetId2}`);
7188
+ logger5.info("Tweet posted successfully (memory saving disabled due to room constraints)");
7076
7189
  return;
7077
7190
  }
7078
- logger4.info(`Generated tweet: ${tweetText}`);
7191
+ logger5.info(`Generated tweet: ${tweetText}`);
7079
7192
  if (this.isDryRun) {
7080
- logger4.info(`[DRY RUN] Would post tweet: ${tweetText}`);
7193
+ logger5.info(`[DRY RUN] Would post tweet: ${tweetText}`);
7081
7194
  return;
7082
7195
  }
7083
7196
  const result = await this.postToTwitter(tweetText, []);
7084
7197
  if (result === null) {
7085
- logger4.info("Skipped posting duplicate tweet");
7198
+ logger5.info("Skipped posting duplicate tweet");
7086
7199
  return;
7087
7200
  }
7088
7201
  const tweetId = result.id;
7089
- logger4.info(`Tweet posted successfully! ID: ${tweetId}`);
7202
+ logger5.info(`Tweet posted successfully! ID: ${tweetId}`);
7090
7203
  if (result) {
7091
7204
  const postedTweetId = createUniqueUuid3(this.runtime, tweetId);
7092
- logger4.info("Tweet posted successfully (memory saving temporarily disabled)");
7205
+ await this.runtime.ensureWorldExists({
7206
+ id: worldId,
7207
+ name: `${this.client.profile?.username}'s Twitter`,
7208
+ agentId: this.runtime.agentId,
7209
+ serverId: userId
7210
+ });
7211
+ await this.runtime.ensureRoomExists({
7212
+ id: roomId,
7213
+ name: `${this.client.profile?.username}'s Timeline`,
7214
+ source: "twitter",
7215
+ type: ChannelType2.FEED,
7216
+ channelId: `${userId}-home`,
7217
+ serverId: userId,
7218
+ worldId
7219
+ });
7220
+ const postedMemory = {
7221
+ id: postedTweetId,
7222
+ entityId: this.runtime.agentId,
7223
+ agentId: this.runtime.agentId,
7224
+ roomId,
7225
+ content: {
7226
+ text: tweetText,
7227
+ source: "twitter",
7228
+ channelType: ChannelType2.FEED,
7229
+ type: "post",
7230
+ metadata: {
7231
+ tweetId,
7232
+ postedAt: Date.now()
7233
+ }
7234
+ },
7235
+ createdAt: Date.now()
7236
+ };
7237
+ await this.runtime.createMemory(postedMemory, "messages");
7238
+ logger5.info("Tweet posted and saved to memory successfully");
7093
7239
  }
7094
7240
  } catch (error) {
7095
- logger4.error("Error generating tweet:", error);
7241
+ logger5.error("Error generating tweet:", error);
7096
7242
  } finally {
7097
7243
  this.isPosting = false;
7098
7244
  }
@@ -7111,7 +7257,7 @@ Generate a single tweet that sounds like YOU would actually write it:`;
7111
7257
  if (lastPost) {
7112
7258
  const lastTweet = await this.client.getTweet(lastPost.id);
7113
7259
  if (lastTweet && lastTweet.text === text) {
7114
- logger4.warn(
7260
+ logger5.warn(
7115
7261
  "Tweet is a duplicate of the last post. Skipping to avoid duplicate."
7116
7262
  );
7117
7263
  return null;
@@ -7121,11 +7267,11 @@ Generate a single tweet that sounds like YOU would actually write it:`;
7121
7267
  if (mediaData && mediaData.length > 0) {
7122
7268
  for (const media of mediaData) {
7123
7269
  try {
7124
- logger4.warn(
7270
+ logger5.warn(
7125
7271
  "Media upload not currently supported with the modern Twitter API"
7126
7272
  );
7127
7273
  } catch (error) {
7128
- logger4.error("Error uploading media:", error);
7274
+ logger5.error("Error uploading media:", error);
7129
7275
  }
7130
7276
  }
7131
7277
  }
@@ -7140,7 +7286,7 @@ Generate a single tweet that sounds like YOU would actually write it:`;
7140
7286
  );
7141
7287
  return result;
7142
7288
  } catch (error) {
7143
- logger4.error("Error posting to Twitter:", error);
7289
+ logger5.error("Error posting to Twitter:", error);
7144
7290
  throw error;
7145
7291
  }
7146
7292
  }
@@ -7154,7 +7300,7 @@ import {
7154
7300
  ModelType as ModelType3,
7155
7301
  parseKeyValueXml
7156
7302
  } from "@elizaos/core";
7157
- import { logger as logger5 } from "@elizaos/core";
7303
+ import { logger as logger6 } from "@elizaos/core";
7158
7304
 
7159
7305
  // src/templates.ts
7160
7306
  var twitterActionTemplate = `
@@ -7234,18 +7380,18 @@ var TwitterTimelineClient = class {
7234
7380
  this.timelineType = timelineMode === "following" /* Following */ ? "following" /* Following */ : "foryou" /* ForYou */;
7235
7381
  }
7236
7382
  async start() {
7237
- logger5.info("Starting Twitter timeline client...");
7383
+ logger6.info("Starting Twitter timeline client...");
7238
7384
  this.isRunning = true;
7239
7385
  const handleTwitterTimelineLoop = () => {
7240
7386
  if (!this.isRunning) {
7241
- logger5.info("Twitter timeline client stopped, exiting loop");
7387
+ logger6.info("Twitter timeline client stopped, exiting loop");
7242
7388
  return;
7243
7389
  }
7244
7390
  const engagementIntervalMinutes = parseInt(
7245
7391
  this.state?.TWITTER_ENGAGEMENT_INTERVAL || getSetting(this.runtime, "TWITTER_ENGAGEMENT_INTERVAL") || process.env.TWITTER_ENGAGEMENT_INTERVAL || "30"
7246
7392
  );
7247
7393
  const actionInterval = engagementIntervalMinutes * 60 * 1e3;
7248
- logger5.info(
7394
+ logger6.info(
7249
7395
  `Timeline client will check every ${engagementIntervalMinutes} minutes`
7250
7396
  );
7251
7397
  this.handleTimeline();
@@ -7256,7 +7402,7 @@ var TwitterTimelineClient = class {
7256
7402
  handleTwitterTimelineLoop();
7257
7403
  }
7258
7404
  async stop() {
7259
- logger5.info("Stopping Twitter timeline client...");
7405
+ logger6.info("Stopping Twitter timeline client...");
7260
7406
  this.isRunning = false;
7261
7407
  }
7262
7408
  async getTimeline(count) {
@@ -7286,9 +7432,9 @@ var TwitterTimelineClient = class {
7286
7432
  };
7287
7433
  }
7288
7434
  async handleTimeline() {
7289
- logger5.info("Starting Twitter timeline processing...");
7435
+ logger6.info("Starting Twitter timeline processing...");
7290
7436
  const tweets = await this.getTimeline(20);
7291
- logger5.info(`Fetched ${tweets.length} tweets from timeline`);
7437
+ logger6.info(`Fetched ${tweets.length} tweets from timeline`);
7292
7438
  const maxActionsPerCycle = parseInt(
7293
7439
  getSetting(this.runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN") || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
7294
7440
  );
@@ -7298,7 +7444,7 @@ var TwitterTimelineClient = class {
7298
7444
  const tweetId = this.createTweetId(this.runtime, tweet);
7299
7445
  const memory = await this.runtime.getMemoryById(tweetId);
7300
7446
  if (memory) {
7301
- logger5.log(`Already processed tweet ID: ${tweet.id}`);
7447
+ logger6.log(`Already processed tweet ID: ${tweet.id}`);
7302
7448
  continue;
7303
7449
  }
7304
7450
  const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
@@ -7322,7 +7468,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7322
7468
  );
7323
7469
  const parsedResponse = parseActionResponseFromText(actionResponse);
7324
7470
  if (!parsedResponse) {
7325
- logger5.debug(`No action response generated for tweet ${tweet.id}`);
7471
+ logger6.debug(`No action response generated for tweet ${tweet.id}`);
7326
7472
  continue;
7327
7473
  }
7328
7474
  tweetDecisions.push({
@@ -7333,7 +7479,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7333
7479
  });
7334
7480
  if (tweetDecisions.length >= maxActionsPerCycle) break;
7335
7481
  } catch (error) {
7336
- logger5.error(`Error processing tweet ${tweet.id}:`, error);
7482
+ logger6.error(`Error processing tweet ${tweet.id}:`, error);
7337
7483
  }
7338
7484
  }
7339
7485
  const rankByActionRelevance = (arr) => {
@@ -7351,7 +7497,7 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7351
7497
  });
7352
7498
  };
7353
7499
  const prioritizedTweets = rankByActionRelevance(tweetDecisions);
7354
- logger5.info(`Processing ${prioritizedTweets.length} tweets with actions`);
7500
+ logger6.info(`Processing ${prioritizedTweets.length} tweets with actions`);
7355
7501
  if (prioritizedTweets.length > 0) {
7356
7502
  const actionSummary = prioritizedTweets.map((td) => {
7357
7503
  const actions = [];
@@ -7361,11 +7507,11 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7361
7507
  if (td.actionResponse.reply) actions.push("REPLY");
7362
7508
  return `Tweet ${td.tweet.id}: ${actions.join(", ")}`;
7363
7509
  });
7364
- logger5.info(`Actions to execute:
7510
+ logger6.info(`Actions to execute:
7365
7511
  ${actionSummary.join("\n")}`);
7366
7512
  }
7367
7513
  await this.processTimelineActions(prioritizedTweets);
7368
- logger5.info("Timeline processing complete");
7514
+ logger6.info("Timeline processing complete");
7369
7515
  }
7370
7516
  async processTimelineActions(tweetDecisions) {
7371
7517
  const results = [];
@@ -7377,6 +7523,15 @@ ${actionSummary.join("\n")}`);
7377
7523
  } of tweetDecisions) {
7378
7524
  const tweetId = this.createTweetId(this.runtime, tweet);
7379
7525
  const executedActions = [];
7526
+ await this.runtime.ensureRoomExists({
7527
+ id: roomId,
7528
+ name: `Twitter conversation ${tweet.conversationId}`,
7529
+ source: "twitter",
7530
+ type: ChannelType3.GROUP,
7531
+ channelId: tweet.conversationId,
7532
+ serverId: tweet.userId,
7533
+ worldId: createUniqueUuid4(this.runtime, tweet.userId)
7534
+ });
7380
7535
  await this.runtime.createMemory(
7381
7536
  {
7382
7537
  id: tweetId,
@@ -7417,7 +7572,7 @@ ${actionSummary.join("\n")}`);
7417
7572
  }
7418
7573
  results.push({ tweetId: tweet.id, actionResponse, executedActions });
7419
7574
  } catch (error) {
7420
- logger5.error(`Error processing actions for tweet ${tweet.id}:`, error);
7575
+ logger6.error(`Error processing actions for tweet ${tweet.id}:`, error);
7421
7576
  }
7422
7577
  }
7423
7578
  return results;
@@ -7461,25 +7616,25 @@ ${actionSummary.join("\n")}`);
7461
7616
  async handleLikeAction(tweet) {
7462
7617
  try {
7463
7618
  if (this.isDryRun) {
7464
- logger5.log(`[DRY RUN] Would have liked tweet ${tweet.id}`);
7619
+ logger6.log(`[DRY RUN] Would have liked tweet ${tweet.id}`);
7465
7620
  return;
7466
7621
  }
7467
7622
  await this.twitterClient.likeTweet(tweet.id);
7468
- logger5.log(`Liked tweet ${tweet.id}`);
7623
+ logger6.log(`Liked tweet ${tweet.id}`);
7469
7624
  } catch (error) {
7470
- logger5.error(`Error liking tweet ${tweet.id}:`, error);
7625
+ logger6.error(`Error liking tweet ${tweet.id}:`, error);
7471
7626
  }
7472
7627
  }
7473
7628
  async handleRetweetAction(tweet) {
7474
7629
  try {
7475
7630
  if (this.isDryRun) {
7476
- logger5.log(`[DRY RUN] Would have retweeted tweet ${tweet.id}`);
7631
+ logger6.log(`[DRY RUN] Would have retweeted tweet ${tweet.id}`);
7477
7632
  return;
7478
7633
  }
7479
7634
  await this.twitterClient.retweet(tweet.id);
7480
- logger5.log(`Retweeted tweet ${tweet.id}`);
7635
+ logger6.log(`Retweeted tweet ${tweet.id}`);
7481
7636
  } catch (error) {
7482
- logger5.error(`Error retweeting tweet ${tweet.id}:`, error);
7637
+ logger6.error(`Error retweeting tweet ${tweet.id}:`, error);
7483
7638
  }
7484
7639
  }
7485
7640
  async handleQuoteAction(tweet) {
@@ -7498,7 +7653,7 @@ ${tweet.text}`;
7498
7653
  const responseObject = parseKeyValueXml(quoteResponse);
7499
7654
  if (responseObject.post) {
7500
7655
  if (this.isDryRun) {
7501
- logger5.log(
7656
+ logger6.log(
7502
7657
  `[DRY RUN] Would have quoted tweet ${tweet.id} with: ${responseObject.post}`
7503
7658
  );
7504
7659
  return;
@@ -7512,9 +7667,9 @@ ${tweet.text}`;
7512
7667
  const body = await result.json();
7513
7668
  const tweetResult = body?.data?.create_tweet?.tweet_results?.result || body?.data || body;
7514
7669
  if (tweetResult) {
7515
- logger5.log("Successfully posted quote tweet");
7670
+ logger6.log("Successfully posted quote tweet");
7516
7671
  } else {
7517
- logger5.error("Quote tweet creation failed:", body);
7672
+ logger6.error("Quote tweet creation failed:", body);
7518
7673
  }
7519
7674
  const tweetId = tweetResult?.id || Date.now().toString();
7520
7675
  const responseId = createUniqueUuid4(this.runtime, tweetId);
@@ -7532,7 +7687,7 @@ ${tweet.text}`;
7532
7687
  await this.runtime.createMemory(responseMemory, "messages");
7533
7688
  }
7534
7689
  } catch (error) {
7535
- logger5.error("Error in quote tweet generation:", error);
7690
+ logger6.error("Error in quote tweet generation:", error);
7536
7691
  }
7537
7692
  }
7538
7693
  async handleReplyAction(tweet) {
@@ -7551,7 +7706,7 @@ ${tweet.text}`;
7551
7706
  const responseObject = parseKeyValueXml(replyResponse);
7552
7707
  if (responseObject.post) {
7553
7708
  if (this.isDryRun) {
7554
- logger5.log(
7709
+ logger6.log(
7555
7710
  `[DRY RUN] Would have replied to tweet ${tweet.id} with: ${responseObject.post}`
7556
7711
  );
7557
7712
  return;
@@ -7563,7 +7718,7 @@ ${tweet.text}`;
7563
7718
  tweet.id
7564
7719
  );
7565
7720
  if (result) {
7566
- logger5.log("Successfully posted reply tweet");
7721
+ logger6.log("Successfully posted reply tweet");
7567
7722
  const responseId = createUniqueUuid4(this.runtime, result.id);
7568
7723
  const responseMemory = {
7569
7724
  id: responseId,
@@ -7580,7 +7735,7 @@ ${tweet.text}`;
7580
7735
  }
7581
7736
  }
7582
7737
  } catch (error) {
7583
- logger5.error("Error in reply tweet generation:", error);
7738
+ logger6.error("Error in reply tweet generation:", error);
7584
7739
  }
7585
7740
  }
7586
7741
  };
@@ -7588,7 +7743,7 @@ ${tweet.text}`;
7588
7743
  // src/discovery.ts
7589
7744
  import {
7590
7745
  createUniqueUuid as createUniqueUuid5,
7591
- logger as logger6,
7746
+ logger as logger7,
7592
7747
  ModelType as ModelType4
7593
7748
  } from "@elizaos/core";
7594
7749
  var TwitterDiscoveryClient = class {
@@ -7602,7 +7757,7 @@ var TwitterDiscoveryClient = class {
7602
7757
  const dryRunSetting = state?.TWITTER_DRY_RUN ?? getSetting(this.runtime, "TWITTER_DRY_RUN") ?? process.env.TWITTER_DRY_RUN;
7603
7758
  this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
7604
7759
  this.config = this.buildDiscoveryConfig();
7605
- logger6.info("Twitter Discovery Config:", {
7760
+ logger7.info("Twitter Discovery Config:", {
7606
7761
  topics: this.config.topics,
7607
7762
  isDryRun: this.isDryRun,
7608
7763
  minFollowerCount: this.config.minFollowerCount,
@@ -7640,7 +7795,7 @@ var TwitterDiscoveryClient = class {
7640
7795
  topics = this.extractTopicsFromBio(character.bio);
7641
7796
  }
7642
7797
  } else {
7643
- logger6.warn(
7798
+ logger7.warn(
7644
7799
  "Character not available in runtime, using default topics for discovery"
7645
7800
  );
7646
7801
  }
@@ -7656,14 +7811,15 @@ var TwitterDiscoveryClient = class {
7656
7811
  getSetting(
7657
7812
  this.runtime,
7658
7813
  "TWITTER_MAX_ENGAGEMENTS_PER_RUN"
7659
- ) || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
7814
+ ) || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "5"
7815
+ // Reduced from 10 to 5
7660
7816
  ),
7661
- likeThreshold: 0.3,
7662
- // Lowered from 0.6
7663
- replyThreshold: 0.5,
7664
- // Lowered from 0.8
7665
- quoteThreshold: 0.7
7666
- // Lowered from 0.85
7817
+ likeThreshold: 0.5,
7818
+ // Increased from 0.3 (be more selective)
7819
+ replyThreshold: 0.7,
7820
+ // Increased from 0.5 (be more selective)
7821
+ quoteThreshold: 0.85
7822
+ // Increased from 0.7 (be more selective)
7667
7823
  };
7668
7824
  }
7669
7825
  extractTopicsFromBio(bio) {
@@ -7684,46 +7840,41 @@ var TwitterDiscoveryClient = class {
7684
7840
  return [...new Set(words)].slice(0, 5);
7685
7841
  }
7686
7842
  async start() {
7687
- logger6.info("Starting Twitter Discovery Client...");
7843
+ logger7.info("Starting Twitter Discovery Client...");
7688
7844
  this.isRunning = true;
7689
7845
  const discoveryLoop = async () => {
7690
7846
  if (!this.isRunning) {
7691
- logger6.info("Discovery client stopped, exiting loop");
7847
+ logger7.info("Discovery client stopped, exiting loop");
7692
7848
  return;
7693
7849
  }
7694
7850
  try {
7695
7851
  await this.runDiscoveryCycle();
7696
7852
  } catch (error) {
7697
- logger6.error("Discovery cycle error:", error);
7853
+ logger7.error("Discovery cycle error:", error);
7698
7854
  }
7699
- const baseInterval = parseInt(
7700
- getSetting(this.runtime, "TWITTER_DISCOVERY_INTERVAL") || process.env.TWITTER_DISCOVERY_INTERVAL || "30"
7701
- );
7702
- const variance = Math.random() * 20 - 10;
7703
- const nextInterval = (baseInterval + variance) * 60 * 1e3;
7704
- logger6.info(
7705
- `Next discovery cycle in ${(baseInterval + variance).toFixed(1)} minutes`
7855
+ const discoveryIntervalMinutes = getRandomInterval(this.runtime, "discovery");
7856
+ const nextInterval = discoveryIntervalMinutes * 60 * 1e3;
7857
+ logger7.log(
7858
+ `Next discovery cycle in ${discoveryIntervalMinutes.toFixed(1)} minutes`
7706
7859
  );
7707
- if (this.isRunning) {
7708
- setTimeout(discoveryLoop, nextInterval);
7709
- }
7860
+ setTimeout(discoveryLoop, nextInterval);
7710
7861
  };
7711
7862
  setTimeout(discoveryLoop, 5e3);
7712
7863
  }
7713
7864
  async stop() {
7714
- logger6.info("Stopping Twitter Discovery Client...");
7865
+ logger7.info("Stopping Twitter Discovery Client...");
7715
7866
  this.isRunning = false;
7716
7867
  }
7717
7868
  async runDiscoveryCycle() {
7718
- logger6.info("Starting Twitter discovery cycle...");
7869
+ logger7.info("Starting Twitter discovery cycle...");
7719
7870
  const discoveries = await this.discoverContent();
7720
7871
  const { tweets, accounts } = discoveries;
7721
- logger6.info(
7872
+ logger7.info(
7722
7873
  `Discovered ${tweets.length} tweets and ${accounts.length} accounts`
7723
7874
  );
7724
7875
  const followedCount = await this.processAccounts(accounts);
7725
7876
  const engagementCount = await this.processTweets(tweets);
7726
- logger6.info(
7877
+ logger7.info(
7727
7878
  `Discovery cycle complete: ${followedCount} follows, ${engagementCount} engagements`
7728
7879
  );
7729
7880
  }
@@ -7735,7 +7886,7 @@ var TwitterDiscoveryClient = class {
7735
7886
  allTweets.push(...topicContent.tweets);
7736
7887
  topicContent.accounts.forEach((acc) => allAccounts.set(acc.user.id, acc));
7737
7888
  } catch (error) {
7738
- logger6.error("Failed to discover from topics:", error);
7889
+ logger7.error("Failed to discover from topics:", error);
7739
7890
  }
7740
7891
  try {
7741
7892
  const threadContent = await this.discoverFromThreads();
@@ -7744,7 +7895,7 @@ var TwitterDiscoveryClient = class {
7744
7895
  (acc) => allAccounts.set(acc.user.id, acc)
7745
7896
  );
7746
7897
  } catch (error) {
7747
- logger6.error("Failed to discover from threads:", error);
7898
+ logger7.error("Failed to discover from threads:", error);
7748
7899
  }
7749
7900
  try {
7750
7901
  const popularContent = await this.discoverFromPopularAccounts();
@@ -7753,7 +7904,7 @@ var TwitterDiscoveryClient = class {
7753
7904
  (acc) => allAccounts.set(acc.user.id, acc)
7754
7905
  );
7755
7906
  } catch (error) {
7756
- logger6.error("Failed to discover from popular accounts:", error);
7907
+ logger7.error("Failed to discover from popular accounts:", error);
7757
7908
  }
7758
7909
  const sortedTweets = allTweets.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, 50);
7759
7910
  const sortedAccounts = Array.from(allAccounts.values()).sort(
@@ -7762,14 +7913,14 @@ var TwitterDiscoveryClient = class {
7762
7913
  return { tweets: sortedTweets, accounts: sortedAccounts };
7763
7914
  }
7764
7915
  async discoverFromTopics() {
7765
- logger6.debug("Discovering from character topics...");
7916
+ logger7.debug("Discovering from character topics...");
7766
7917
  const tweets = [];
7767
7918
  const accounts = /* @__PURE__ */ new Map();
7768
7919
  for (const topic of this.config.topics.slice(0, 5)) {
7769
7920
  try {
7770
7921
  const searchTopic = this.sanitizeTopic(topic);
7771
7922
  const popularQuery = `${searchTopic} -is:retweet -is:reply lang:en`;
7772
- logger6.debug(`Searching popular tweets for topic: ${topic}`);
7923
+ logger7.debug(`Searching popular tweets for topic: ${topic}`);
7773
7924
  const popularResults = await this.twitterClient.fetchSearchTweets(
7774
7925
  popularQuery,
7775
7926
  20,
@@ -7779,44 +7930,66 @@ var TwitterDiscoveryClient = class {
7779
7930
  if ((tweet.likes || 0) < 10) continue;
7780
7931
  const scored = this.scoreTweet(tweet, "topic");
7781
7932
  tweets.push(scored);
7933
+ const authorUsername = tweet.username;
7934
+ const authorName = tweet.name || tweet.username;
7935
+ const estimatedFollowers = Math.max(
7936
+ 1e3,
7937
+ // minimum estimate
7938
+ (tweet.likes || 0) * 100
7939
+ // rough estimate: 100 followers per like
7940
+ );
7941
+ const account = this.scoreAccount({
7942
+ id: tweet.userId,
7943
+ username: authorUsername,
7944
+ name: authorName,
7945
+ followersCount: estimatedFollowers
7946
+ });
7947
+ if (account.qualityScore > 0.3) {
7948
+ accounts.set(tweet.userId, account);
7949
+ }
7782
7950
  }
7783
- const verifiedQuery = `${searchTopic} -is:retweet lang:en is:verified`;
7784
- logger6.debug(`Searching verified accounts for topic: ${topic}`);
7785
- const verifiedResults = await this.twitterClient.fetchSearchTweets(
7786
- verifiedQuery,
7787
- 10,
7951
+ const engagedQuery = `${searchTopic} -is:retweet lang:en`;
7952
+ logger7.debug(`Searching engaged tweets for topic: ${topic}`);
7953
+ const engagedResults = await this.twitterClient.fetchSearchTweets(
7954
+ engagedQuery,
7955
+ 15,
7788
7956
  1 /* Latest */
7789
7957
  );
7790
- for (const tweet of verifiedResults.tweets) {
7958
+ for (const tweet of engagedResults.tweets) {
7959
+ if ((tweet.likes || 0) < 5) continue;
7791
7960
  const scored = this.scoreTweet(tweet, "topic");
7792
7961
  tweets.push(scored);
7793
7962
  const authorUsername = tweet.username;
7794
7963
  const authorName = tweet.name || tweet.username;
7964
+ const estimatedFollowers = Math.max(
7965
+ 500,
7966
+ // minimum for engaged tweets
7967
+ (tweet.likes || 0) * 50
7968
+ );
7795
7969
  const account = this.scoreAccount({
7796
7970
  id: tweet.userId,
7797
7971
  username: authorUsername,
7798
7972
  name: authorName,
7799
- followersCount: 1e3
7800
- // Default estimate for verified accounts
7973
+ followersCount: estimatedFollowers
7801
7974
  });
7802
- if (account.qualityScore > 0.5) {
7975
+ if (account.qualityScore > 0.2) {
7803
7976
  accounts.set(tweet.userId, account);
7804
7977
  }
7805
7978
  }
7806
7979
  } catch (error) {
7807
- logger6.error(`Failed to search topic ${topic}:`, error);
7980
+ logger7.error(`Failed to search topic ${topic}:`, error);
7808
7981
  }
7809
7982
  }
7810
7983
  return { tweets, accounts: Array.from(accounts.values()) };
7811
7984
  }
7812
7985
  async discoverFromThreads() {
7813
- logger6.debug("Discovering from conversation threads...");
7986
+ logger7.debug("Discovering from conversation threads...");
7814
7987
  const tweets = [];
7815
7988
  const accounts = /* @__PURE__ */ new Map();
7816
7989
  const topicQuery = this.config.topics.slice(0, 3).map((t) => this.sanitizeTopic(t)).join(" OR ");
7817
7990
  try {
7818
7991
  const viralQuery = `(${topicQuery}) -is:retweet has:mentions`;
7819
- logger6.debug(`Searching viral threads with query: ${viralQuery}`);
7992
+ logger7.debug(`Searching viral threads with query: ${viralQuery}`);
7820
7993
  const searchResults = await this.twitterClient.fetchSearchTweets(
7821
7994
  viralQuery,
7822
7995
  15,
@@ -7839,19 +8012,19 @@ var TwitterDiscoveryClient = class {
7839
8012
  }
7840
8013
  }
7841
8014
  } catch (error) {
7842
- logger6.error("Failed to discover threads:", error);
8015
+ logger7.error("Failed to discover threads:", error);
7843
8016
  }
7844
8017
  return { tweets, accounts: Array.from(accounts.values()) };
7845
8018
  }
7846
8019
  async discoverFromPopularAccounts() {
7847
- logger6.debug("Discovering from popular accounts in topics...");
8020
+ logger7.debug("Discovering from popular accounts in topics...");
7848
8021
  const tweets = [];
7849
8022
  const accounts = /* @__PURE__ */ new Map();
7850
8023
  for (const topic of this.config.topics.slice(0, 3)) {
7851
8024
  try {
7852
8025
  const searchTopic = this.sanitizeTopic(topic);
7853
8026
  const influencerQuery = `${searchTopic} -is:retweet lang:en`;
7854
- logger6.debug(`Searching for influencers in topic: ${topic}`);
8027
+ logger7.debug(`Searching for influencers in topic: ${topic}`);
7855
8028
  const results = await this.twitterClient.fetchSearchTweets(
7856
8029
  influencerQuery,
7857
8030
  10,
@@ -7878,7 +8051,7 @@ var TwitterDiscoveryClient = class {
7878
8051
  }
7879
8052
  }
7880
8053
  } catch (error) {
7881
- logger6.error(
8054
+ logger7.error(
7882
8055
  `Failed to discover popular accounts for ${topic}:`,
7883
8056
  error
7884
8057
  );
@@ -7942,18 +8115,35 @@ var TwitterDiscoveryClient = class {
7942
8115
  }
7943
8116
  async processAccounts(accounts) {
7944
8117
  let followedCount = 0;
7945
- for (const scoredAccount of accounts) {
8118
+ const sortedAccounts = accounts.sort((a, b) => {
8119
+ const scoreA = a.qualityScore + a.relevanceScore;
8120
+ const scoreB = b.qualityScore + b.relevanceScore;
8121
+ return scoreB - scoreA;
8122
+ });
8123
+ for (const scoredAccount of sortedAccounts) {
7946
8124
  if (followedCount >= this.config.maxFollowsPerCycle) break;
8125
+ if (scoredAccount.user.followersCount < this.config.minFollowerCount) {
8126
+ logger7.debug(
8127
+ `Skipping @${scoredAccount.user.username} - below minimum follower count (${scoredAccount.user.followersCount} < ${this.config.minFollowerCount})`
8128
+ );
8129
+ continue;
8130
+ }
8131
+ if (scoredAccount.qualityScore < 0.2) {
8132
+ logger7.debug(
8133
+ `Skipping @${scoredAccount.user.username} - quality score too low (${scoredAccount.qualityScore.toFixed(2)})`
8134
+ );
8135
+ continue;
8136
+ }
7947
8137
  try {
7948
8138
  const isFollowing = await this.checkIfFollowing(scoredAccount.user.id);
7949
8139
  if (isFollowing) continue;
7950
8140
  if (this.isDryRun) {
7951
- logger6.info(
8141
+ logger7.info(
7952
8142
  `[DRY RUN] Would follow @${scoredAccount.user.username} (quality: ${scoredAccount.qualityScore.toFixed(2)}, relevance: ${scoredAccount.relevanceScore.toFixed(2)})`
7953
8143
  );
7954
8144
  } else {
7955
8145
  await this.twitterClient.followUser(scoredAccount.user.id);
7956
- logger6.info(
8146
+ logger7.info(
7957
8147
  `Followed @${scoredAccount.user.username} (quality: ${scoredAccount.qualityScore.toFixed(2)}, relevance: ${scoredAccount.relevanceScore.toFixed(2)})`
7958
8148
  );
7959
8149
  await this.saveFollowMemory(scoredAccount.user);
@@ -7961,7 +8151,7 @@ var TwitterDiscoveryClient = class {
7961
8151
  followedCount++;
7962
8152
  await this.delay(2e3 + Math.random() * 3e3);
7963
8153
  } catch (error) {
7964
- logger6.error(
8154
+ logger7.error(
7965
8155
  `Failed to follow @${scoredAccount.user.username}:`,
7966
8156
  error
7967
8157
  );
@@ -7981,7 +8171,7 @@ var TwitterDiscoveryClient = class {
7981
8171
  );
7982
8172
  const existingMemory = await this.runtime.getMemoryById(tweetMemoryId);
7983
8173
  if (existingMemory) {
7984
- logger6.debug(
8174
+ logger7.debug(
7985
8175
  `Already engaged with tweet ${scoredTweet.tweet.id}, skipping`
7986
8176
  );
7987
8177
  continue;
@@ -7989,12 +8179,12 @@ var TwitterDiscoveryClient = class {
7989
8179
  switch (scoredTweet.engagementType) {
7990
8180
  case "like":
7991
8181
  if (this.isDryRun) {
7992
- logger6.info(
8182
+ logger7.info(
7993
8183
  `[DRY RUN] Would like tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
7994
8184
  );
7995
8185
  } else {
7996
8186
  await this.twitterClient.likeTweet(scoredTweet.tweet.id);
7997
- logger6.info(
8187
+ logger7.info(
7998
8188
  `Liked tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
7999
8189
  );
8000
8190
  }
@@ -8002,7 +8192,7 @@ var TwitterDiscoveryClient = class {
8002
8192
  case "reply":
8003
8193
  const replyText = await this.generateReply(scoredTweet.tweet);
8004
8194
  if (this.isDryRun) {
8005
- logger6.info(
8195
+ logger7.info(
8006
8196
  `[DRY RUN] Would reply to tweet ${scoredTweet.tweet.id} with: "${replyText}"`
8007
8197
  );
8008
8198
  } else {
@@ -8010,13 +8200,13 @@ var TwitterDiscoveryClient = class {
8010
8200
  replyText,
8011
8201
  scoredTweet.tweet.id
8012
8202
  );
8013
- logger6.info(`Replied to tweet: ${scoredTweet.tweet.id}`);
8203
+ logger7.info(`Replied to tweet: ${scoredTweet.tweet.id}`);
8014
8204
  }
8015
8205
  break;
8016
8206
  case "quote":
8017
8207
  const quoteText = await this.generateQuote(scoredTweet.tweet);
8018
8208
  if (this.isDryRun) {
8019
- logger6.info(
8209
+ logger7.info(
8020
8210
  `[DRY RUN] Would quote tweet ${scoredTweet.tweet.id} with: "${quoteText}"`
8021
8211
  );
8022
8212
  } else {
@@ -8024,7 +8214,7 @@ var TwitterDiscoveryClient = class {
8024
8214
  quoteText,
8025
8215
  scoredTweet.tweet.id
8026
8216
  );
8027
- logger6.info(`Quoted tweet: ${scoredTweet.tweet.id}`);
8217
+ logger7.info(`Quoted tweet: ${scoredTweet.tweet.id}`);
8028
8218
  }
8029
8219
  break;
8030
8220
  }
@@ -8035,7 +8225,7 @@ var TwitterDiscoveryClient = class {
8035
8225
  engagementCount++;
8036
8226
  await this.delay(3e3 + Math.random() * 5e3);
8037
8227
  } catch (error) {
8038
- logger6.error(
8228
+ logger7.error(
8039
8229
  `Failed to engage with tweet ${scoredTweet.tweet.id}:`,
8040
8230
  error
8041
8231
  );
@@ -8120,43 +8310,12 @@ Quote tweet:`;
8120
8310
  return response.trim();
8121
8311
  }
8122
8312
  async saveEngagementMemory(tweet, engagementType) {
8123
- const memoryId = await this.runtime.createMemory(
8124
- {
8125
- id: createUniqueUuid5(this.runtime, tweet.id),
8126
- entityId: createUniqueUuid5(this.runtime, tweet.userId),
8127
- content: {
8128
- text: `${engagementType} tweet from @${tweet.username}: ${tweet.text}`,
8129
- metadata: {
8130
- tweetId: tweet.id,
8131
- engagementType,
8132
- source: "discovery",
8133
- isDryRun: this.isDryRun
8134
- }
8135
- },
8136
- roomId: createUniqueUuid5(this.runtime, tweet.conversationId)
8137
- },
8138
- "messages"
8139
- );
8313
+ logger7.debug(`[Discovery] Would save engagement memory for ${engagementType} on tweet ${tweet.id}`);
8314
+ return;
8140
8315
  }
8141
8316
  async saveFollowMemory(user) {
8142
- const memoryId = await this.runtime.createMemory(
8143
- {
8144
- entityId: createUniqueUuid5(this.runtime, user.id),
8145
- content: {
8146
- text: `followed twitter user ${user.id} @${user.username}`,
8147
- metadata: {
8148
- userId: user.id,
8149
- username: user.username,
8150
- name: user.name,
8151
- followersCount: user.followersCount,
8152
- source: "discovery",
8153
- isDryRun: this.isDryRun
8154
- }
8155
- },
8156
- roomId: createUniqueUuid5(this.runtime, `twitter-follows`)
8157
- },
8158
- "messages"
8159
- );
8317
+ logger7.debug(`[Discovery] Would save follow memory for @${user.username}`);
8318
+ return;
8160
8319
  }
8161
8320
  delay(ms) {
8162
8321
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -8167,7 +8326,7 @@ Quote tweet:`;
8167
8326
  import {
8168
8327
  ChannelType as ChannelType4,
8169
8328
  createUniqueUuid as createUniqueUuid6,
8170
- logger as logger7
8329
+ logger as logger8
8171
8330
  } from "@elizaos/core";
8172
8331
  var RequestQueue = class {
8173
8332
  constructor() {
@@ -8212,7 +8371,7 @@ var RequestQueue = class {
8212
8371
  await request();
8213
8372
  this.retryAttempts.delete(request);
8214
8373
  } catch (error) {
8215
- logger7.error("Error processing request:", error);
8374
+ logger8.error("Error processing request:", error);
8216
8375
  const retryCount = (this.retryAttempts.get(request) || 0) + 1;
8217
8376
  if (retryCount < this.maxRetries) {
8218
8377
  this.retryAttempts.set(request, retryCount);
@@ -8220,7 +8379,7 @@ var RequestQueue = class {
8220
8379
  await this.exponentialBackoff(retryCount);
8221
8380
  break;
8222
8381
  } else {
8223
- logger7.error(
8382
+ logger8.error(
8224
8383
  `Max retries (${this.maxRetries}) exceeded for request, skipping`
8225
8384
  );
8226
8385
  this.retryAttempts.delete(request);
@@ -8278,7 +8437,7 @@ var _ClientBase = class _ClientBase {
8278
8437
  */
8279
8438
  async cacheTweet(tweet) {
8280
8439
  if (!tweet) {
8281
- logger7.warn("Tweet is undefined, skipping cache");
8440
+ logger8.warn("Tweet is undefined, skipping cache");
8282
8441
  return;
8283
8442
  }
8284
8443
  this.runtime.setCache(`twitter/tweets/${tweet.id}`, tweet);
@@ -8343,7 +8502,7 @@ var _ClientBase = class _ClientBase {
8343
8502
  let lastError = null;
8344
8503
  while (retryCount < maxRetries) {
8345
8504
  try {
8346
- logger7.log("Initializing Twitter API v2 client");
8505
+ logger8.log("Initializing Twitter API v2 client");
8347
8506
  await this.twitterClient.login(
8348
8507
  "",
8349
8508
  // username not needed for API v2
@@ -8359,18 +8518,18 @@ var _ClientBase = class _ClientBase {
8359
8518
  accessTokenSecret
8360
8519
  );
8361
8520
  if (await this.twitterClient.isLoggedIn()) {
8362
- logger7.info("Successfully authenticated with Twitter API v2");
8521
+ logger8.info("Successfully authenticated with Twitter API v2");
8363
8522
  break;
8364
8523
  }
8365
8524
  } catch (error) {
8366
8525
  lastError = error instanceof Error ? error : new Error(String(error));
8367
- logger7.error(
8526
+ logger8.error(
8368
8527
  `Authentication attempt ${retryCount + 1} failed: ${lastError.message}`
8369
8528
  );
8370
8529
  retryCount++;
8371
8530
  if (retryCount < maxRetries) {
8372
8531
  const delay = 2 ** retryCount * 1e3;
8373
- logger7.info(`Retrying in ${delay / 1e3} seconds...`);
8532
+ logger8.info(`Retrying in ${delay / 1e3} seconds...`);
8374
8533
  await new Promise((resolve) => setTimeout(resolve, delay));
8375
8534
  }
8376
8535
  }
@@ -8382,13 +8541,13 @@ var _ClientBase = class _ClientBase {
8382
8541
  }
8383
8542
  const profile = await this.twitterClient.me();
8384
8543
  if (profile) {
8385
- logger7.log("Twitter user ID:", profile.userId);
8386
- logger7.log("Twitter loaded:", JSON.stringify(profile, null, 10));
8544
+ logger8.log("Twitter user ID:", profile.userId);
8545
+ logger8.log("Twitter loaded:", JSON.stringify(profile, null, 10));
8387
8546
  const agentId = this.runtime.agentId;
8388
8547
  const entity = await this.runtime.getEntityById(agentId);
8389
8548
  const entityMetadata = entity?.metadata;
8390
8549
  if (entityMetadata?.twitter?.userName !== profile.username) {
8391
- logger7.log(
8550
+ logger8.log(
8392
8551
  "Updating Agents known X/twitter handle",
8393
8552
  profile.username,
8394
8553
  "was",
@@ -8427,7 +8586,7 @@ var _ClientBase = class _ClientBase {
8427
8586
  await this.populateTimeline();
8428
8587
  }
8429
8588
  async fetchOwnPosts(count) {
8430
- logger7.debug("fetching own posts");
8589
+ logger8.debug("fetching own posts");
8431
8590
  const homeTimeline = await this.twitterClient.getUserTweets(
8432
8591
  this.profile.id,
8433
8592
  count
@@ -8438,7 +8597,7 @@ var _ClientBase = class _ClientBase {
8438
8597
  * Fetch timeline for twitter account, optionally only from followed accounts
8439
8598
  */
8440
8599
  async fetchHomeTimeline(count, following) {
8441
- logger7.debug("fetching home timeline");
8600
+ logger8.debug("fetching home timeline");
8442
8601
  const homeTimeline = following ? await this.twitterClient.fetchFollowingTimeline(count, []) : await this.twitterClient.fetchHomeTimeline(count, []);
8443
8602
  return homeTimeline;
8444
8603
  }
@@ -8461,16 +8620,16 @@ var _ClientBase = class _ClientBase {
8461
8620
  );
8462
8621
  return result ?? { tweets: [] };
8463
8622
  } catch (error) {
8464
- logger7.error("Error fetching search tweets:", error);
8623
+ logger8.error("Error fetching search tweets:", error);
8465
8624
  return { tweets: [] };
8466
8625
  }
8467
8626
  } catch (error) {
8468
- logger7.error("Error fetching search tweets:", error);
8627
+ logger8.error("Error fetching search tweets:", error);
8469
8628
  return { tweets: [] };
8470
8629
  }
8471
8630
  }
8472
8631
  async populateTimeline() {
8473
- logger7.debug("populating timeline...");
8632
+ logger8.debug("populating timeline...");
8474
8633
  const cachedTimeline = await this.getCachedTimeline();
8475
8634
  if (cachedTimeline) {
8476
8635
  const existingMemories2 = await this.runtime.getMemoriesByRoomIds({
@@ -8490,7 +8649,7 @@ var _ClientBase = class _ClientBase {
8490
8649
  (tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(createUniqueUuid6(this.runtime, tweet.id))
8491
8650
  );
8492
8651
  for (const tweet of tweetsToSave2) {
8493
- logger7.log("Saving Tweet", tweet.id);
8652
+ logger8.log("Saving Tweet", tweet.id);
8494
8653
  if (tweet.userId === this.profile.id) {
8495
8654
  continue;
8496
8655
  }
@@ -8538,7 +8697,7 @@ var _ClientBase = class _ClientBase {
8538
8697
  );
8539
8698
  await this.cacheTweet(tweet);
8540
8699
  }
8541
- logger7.log(
8700
+ logger8.log(
8542
8701
  `Populated ${tweetsToSave2.length} missing tweets from the cache.`
8543
8702
  );
8544
8703
  return;
@@ -8567,11 +8726,11 @@ var _ClientBase = class _ClientBase {
8567
8726
  const tweetsToSave = allTweets.filter(
8568
8727
  (tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(createUniqueUuid6(this.runtime, tweet.id))
8569
8728
  );
8570
- logger7.debug({
8729
+ logger8.debug({
8571
8730
  processingTweets: tweetsToSave.map((tweet) => tweet.id).join(",")
8572
8731
  });
8573
8732
  for (const tweet of tweetsToSave) {
8574
- logger7.log("Saving Tweet", tweet.id);
8733
+ logger8.log("Saving Tweet", tweet.id);
8575
8734
  if (tweet.userId === this.profile.id) {
8576
8735
  continue;
8577
8736
  }
@@ -8631,7 +8790,7 @@ var _ClientBase = class _ClientBase {
8631
8790
  unique: false
8632
8791
  });
8633
8792
  if (recentMessage.length > 0 && recentMessage[0].content === message.content) {
8634
- logger7.debug("Message already saved", recentMessage[0].id);
8793
+ logger8.debug("Message already saved", recentMessage[0].id);
8635
8794
  } else {
8636
8795
  await this.runtime.createMemory(message, "messages");
8637
8796
  }
@@ -8704,7 +8863,7 @@ var _ClientBase = class _ClientBase {
8704
8863
  });
8705
8864
  return profile;
8706
8865
  } catch (error) {
8707
- logger7.error("Error fetching Twitter profile:", error);
8866
+ logger8.error("Error fetching Twitter profile:", error);
8708
8867
  throw error;
8709
8868
  }
8710
8869
  }
@@ -8725,7 +8884,7 @@ var _ClientBase = class _ClientBase {
8725
8884
  (tweet) => this.formatTweetToInteraction(tweet)
8726
8885
  );
8727
8886
  } catch (error) {
8728
- logger7.error("Error fetching Twitter interactions:", error);
8887
+ logger8.error("Error fetching Twitter interactions:", error);
8729
8888
  return [];
8730
8889
  }
8731
8890
  }
@@ -8755,42 +8914,42 @@ var TwitterClientInstance = class {
8755
8914
  constructor(runtime, state) {
8756
8915
  this.client = new ClientBase(runtime, state);
8757
8916
  const postEnabledSetting = getSetting(runtime, "TWITTER_ENABLE_POST") ?? process.env.TWITTER_ENABLE_POST;
8758
- logger8.debug(
8917
+ logger9.debug(
8759
8918
  `TWITTER_ENABLE_POST setting value: ${JSON.stringify(postEnabledSetting)}, type: ${typeof postEnabledSetting}`
8760
8919
  );
8761
8920
  const postEnabled = postEnabledSetting === "true" || postEnabledSetting === true;
8762
8921
  if (postEnabled) {
8763
- logger8.info("Twitter posting is ENABLED - creating post client");
8922
+ logger9.info("Twitter posting is ENABLED - creating post client");
8764
8923
  this.post = new TwitterPostClient(this.client, runtime, state);
8765
8924
  } else {
8766
- logger8.info(
8925
+ logger9.info(
8767
8926
  "Twitter posting is DISABLED - set TWITTER_ENABLE_POST=true to enable automatic posting"
8768
8927
  );
8769
8928
  }
8770
8929
  const repliesEnabled = (getSetting(runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
8771
8930
  if (repliesEnabled) {
8772
- logger8.info("Twitter replies/interactions are ENABLED");
8931
+ logger9.info("Twitter replies/interactions are ENABLED");
8773
8932
  this.interaction = new TwitterInteractionClient(
8774
8933
  this.client,
8775
8934
  runtime,
8776
8935
  state
8777
8936
  );
8778
8937
  } else {
8779
- logger8.info("Twitter replies/interactions are DISABLED");
8938
+ logger9.info("Twitter replies/interactions are DISABLED");
8780
8939
  }
8781
8940
  const actionsEnabled = (getSetting(runtime, "TWITTER_ENABLE_ACTIONS") ?? process.env.TWITTER_ENABLE_ACTIONS) === "true";
8782
8941
  if (actionsEnabled) {
8783
- logger8.info("Twitter timeline actions are ENABLED");
8942
+ logger9.info("Twitter timeline actions are ENABLED");
8784
8943
  this.timeline = new TwitterTimelineClient(this.client, runtime, state);
8785
8944
  } else {
8786
- logger8.info("Twitter timeline actions are DISABLED");
8945
+ logger9.info("Twitter timeline actions are DISABLED");
8787
8946
  }
8788
8947
  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";
8789
8948
  if (discoveryEnabled) {
8790
- logger8.info("Twitter discovery service is ENABLED");
8949
+ logger9.info("Twitter discovery service is ENABLED");
8791
8950
  this.discovery = new TwitterDiscoveryClient(this.client, runtime, state);
8792
8951
  } else {
8793
- logger8.info(
8952
+ logger9.info(
8794
8953
  "Twitter discovery service is DISABLED - set TWITTER_ENABLE_DISCOVERY=true to enable"
8795
8954
  );
8796
8955
  }
@@ -8807,28 +8966,28 @@ var _TwitterService = class _TwitterService extends Service {
8807
8966
  service.runtime = runtime;
8808
8967
  try {
8809
8968
  await validateTwitterConfig(runtime);
8810
- logger8.log("\u2705 Twitter configuration validated successfully");
8969
+ logger9.log("\u2705 Twitter configuration validated successfully");
8811
8970
  service.twitterClient = new TwitterClientInstance(runtime, {});
8812
8971
  await service.twitterClient.client.init();
8813
8972
  if (service.twitterClient.post) {
8814
- logger8.log("\u{1F4EE} Starting Twitter post client...");
8973
+ logger9.log("\u{1F4EE} Starting Twitter post client...");
8815
8974
  await service.twitterClient.post.start();
8816
8975
  }
8817
8976
  if (service.twitterClient.interaction) {
8818
- logger8.log("\u{1F4AC} Starting Twitter interaction client...");
8977
+ logger9.log("\u{1F4AC} Starting Twitter interaction client...");
8819
8978
  await service.twitterClient.interaction.start();
8820
8979
  }
8821
8980
  if (service.twitterClient.timeline) {
8822
- logger8.log("\u{1F4CA} Starting Twitter timeline client...");
8981
+ logger9.log("\u{1F4CA} Starting Twitter timeline client...");
8823
8982
  await service.twitterClient.timeline.start();
8824
8983
  }
8825
8984
  if (service.twitterClient.discovery) {
8826
- logger8.log("\u{1F50D} Starting Twitter discovery client...");
8985
+ logger9.log("\u{1F50D} Starting Twitter discovery client...");
8827
8986
  await service.twitterClient.discovery.start();
8828
8987
  }
8829
- logger8.log("\u2705 Twitter service started successfully");
8988
+ logger9.log("\u2705 Twitter service started successfully");
8830
8989
  } catch (error) {
8831
- logger8.error("\u{1F6A8} Failed to start Twitter service:", error);
8990
+ logger9.error("\u{1F6A8} Failed to start Twitter service:", error);
8832
8991
  throw error;
8833
8992
  }
8834
8993
  return service;
@@ -8846,7 +9005,7 @@ var _TwitterService = class _TwitterService extends Service {
8846
9005
  if (this.twitterClient?.discovery) {
8847
9006
  await this.twitterClient.discovery.stop();
8848
9007
  }
8849
- logger8.log("Twitter service stopped");
9008
+ logger9.log("Twitter service stopped");
8850
9009
  }
8851
9010
  };
8852
9011
  _TwitterService.serviceType = "twitter";
@@ -8854,7 +9013,7 @@ var TwitterService = _TwitterService;
8854
9013
 
8855
9014
  // src/actions/postTweet.ts
8856
9015
  import {
8857
- logger as logger9,
9016
+ logger as logger10,
8858
9017
  ModelType as ModelType5
8859
9018
  } from "@elizaos/core";
8860
9019
  var postTweetAction = {
@@ -8867,8 +9026,8 @@ var postTweetAction = {
8867
9026
  "SHARE_ON_TWITTER"
8868
9027
  ],
8869
9028
  validate: async (runtime, message) => {
8870
- logger9.debug("Validating POST_TWEET action");
8871
- logger9.debug("Message details:", {
9029
+ logger10.debug("Validating POST_TWEET action");
9030
+ logger10.debug("Message details:", {
8872
9031
  hasContent: !!message.content,
8873
9032
  contentType: message.content?.type,
8874
9033
  hasText: !!message.content?.text,
@@ -8877,20 +9036,24 @@ var postTweetAction = {
8877
9036
  roomId: message.roomId,
8878
9037
  entityId: message.entityId
8879
9038
  });
9039
+ if (message.content?.type === "post" && !message.content?.text) {
9040
+ logger10.debug("Skipping validation for provider context (empty post type)");
9041
+ return false;
9042
+ }
8880
9043
  const text = message.content?.text?.trim();
8881
9044
  if (!text || text.length === 0) {
8882
- logger9.error("No text content for tweet");
8883
- logger9.debug("Stack trace:", new Error().stack);
9045
+ logger10.error("No text content for tweet");
9046
+ logger10.debug("Stack trace:", new Error().stack);
8884
9047
  return false;
8885
9048
  }
8886
9049
  if (text.length > 280) {
8887
- logger9.warn(`Tweet too long: ${text.length} characters`);
9050
+ logger10.warn(`Tweet too long: ${text.length} characters`);
8888
9051
  }
8889
9052
  return true;
8890
9053
  },
8891
9054
  description: "Post a tweet on Twitter",
8892
9055
  handler: async (runtime, message, state, _options, callback) => {
8893
- logger9.info("Executing POST_TWEET action");
9056
+ logger10.info("Executing POST_TWEET action");
8894
9057
  try {
8895
9058
  const client = new ClientBase(runtime, {});
8896
9059
  if (!client.twitterClient) {
@@ -8898,7 +9061,7 @@ var postTweetAction = {
8898
9061
  }
8899
9062
  let text = message.content?.text?.trim();
8900
9063
  if (!text) {
8901
- logger9.error("No text content for tweet");
9064
+ logger10.error("No text content for tweet");
8902
9065
  if (callback) {
8903
9066
  callback({
8904
9067
  text: "I need something to tweet! Please provide the text.",
@@ -8908,7 +9071,7 @@ var postTweetAction = {
8908
9071
  return;
8909
9072
  }
8910
9073
  if (text.length > 280) {
8911
- logger9.info(`Truncating tweet from ${text.length} to 280 characters`);
9074
+ logger10.info(`Truncating tweet from ${text.length} to 280 characters`);
8912
9075
  const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
8913
9076
  let truncated = "";
8914
9077
  for (const sentence of sentences) {
@@ -8919,7 +9082,7 @@ var postTweetAction = {
8919
9082
  }
8920
9083
  }
8921
9084
  text = truncated.trim() || text.substring(0, 277) + "...";
8922
- logger9.info(`Truncated tweet: ${text}`);
9085
+ logger10.info(`Truncated tweet: ${text}`);
8923
9086
  }
8924
9087
  if (!client.profile) {
8925
9088
  throw new Error(
@@ -8975,7 +9138,7 @@ Tweet:`;
8975
9138
  tweetId = Date.now().toString();
8976
9139
  }
8977
9140
  const tweetUrl = `https://twitter.com/${client.profile.username}/status/${tweetId}`;
8978
- logger9.info(`Successfully posted tweet: ${tweetId}`);
9141
+ logger10.info(`Successfully posted tweet: ${tweetId}`);
8979
9142
  await runtime.createMemory(
8980
9143
  {
8981
9144
  entityId: runtime.agentId,
@@ -9005,7 +9168,7 @@ View it here: ${tweetUrl}`,
9005
9168
  throw new Error("Failed to post tweet - no response data");
9006
9169
  }
9007
9170
  } catch (error) {
9008
- logger9.error("Error posting tweet:", error);
9171
+ logger10.error("Error posting tweet:", error);
9009
9172
  if (callback) {
9010
9173
  await callback({
9011
9174
  text: `Sorry, I couldn't post the tweet. Error: ${error.message}`,
@@ -9071,7 +9234,7 @@ var TwitterPlugin = {
9071
9234
  actions: [postTweetAction],
9072
9235
  services: [TwitterService],
9073
9236
  init: async (_config, runtime) => {
9074
- logger10.log("\u{1F527} Initializing Twitter plugin...");
9237
+ logger11.log("\u{1F527} Initializing Twitter plugin...");
9075
9238
  const hasGetSetting = runtime && typeof runtime.getSetting === "function";
9076
9239
  const apiKey = hasGetSetting ? runtime.getSetting("TWITTER_API_KEY") : process.env.TWITTER_API_KEY;
9077
9240
  const apiSecretKey = hasGetSetting ? runtime.getSetting("TWITTER_API_SECRET_KEY") : process.env.TWITTER_API_SECRET_KEY;
@@ -9083,14 +9246,14 @@ var TwitterPlugin = {
9083
9246
  if (!apiSecretKey) missing.push("TWITTER_API_SECRET_KEY");
9084
9247
  if (!accessToken) missing.push("TWITTER_ACCESS_TOKEN");
9085
9248
  if (!accessTokenSecret) missing.push("TWITTER_ACCESS_TOKEN_SECRET");
9086
- logger10.warn(
9249
+ logger11.warn(
9087
9250
  `Twitter API credentials not configured - Twitter functionality will be limited. Missing: ${missing.join(", ")}`
9088
9251
  );
9089
- logger10.warn(
9252
+ logger11.warn(
9090
9253
  "To enable Twitter functionality, please provide the missing credentials in your .env file"
9091
9254
  );
9092
9255
  } else {
9093
- logger10.log("\u2705 Twitter credentials found");
9256
+ logger11.log("\u2705 Twitter credentials found");
9094
9257
  }
9095
9258
  }
9096
9259
  };