@elizaos/plugin-twitter 1.2.8 → 1.2.11

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
@@ -452,7 +452,10 @@ async function followUser(username, auth) {
452
452
  if (!meResponse.data) {
453
453
  throw new Error("Failed to get authenticated user");
454
454
  }
455
- const result = await client.v2.follow(meResponse.data.id, userResponse.data.id);
455
+ const result = await client.v2.follow(
456
+ meResponse.data.id,
457
+ userResponse.data.id
458
+ );
456
459
  return new Response(JSON.stringify(result), {
457
460
  status: result.data?.following ? 200 : 400,
458
461
  headers: new Headers({ "Content-Type": "application/json" })
@@ -748,7 +751,9 @@ async function fetchTweetsAndReplies(userId, maxTweets, cursor, auth) {
748
751
  next: response.meta.next_token
749
752
  };
750
753
  } catch (error) {
751
- throw new Error(`Failed to fetch tweets and replies: ${error?.message || error}`);
754
+ throw new Error(
755
+ `Failed to fetch tweets and replies: ${error?.message || error}`
756
+ );
752
757
  }
753
758
  }
754
759
  async function createCreateTweetRequestV2(text, auth, tweetId, options) {
@@ -822,9 +827,15 @@ function parseTweetV2ToV1(tweetV2, includes) {
822
827
  isReply: tweetV2.referenced_tweets?.some((ref) => ref.type === "replied_to") ?? false,
823
828
  isRetweet: tweetV2.referenced_tweets?.some((ref) => ref.type === "retweeted") ?? false,
824
829
  isQuoted: tweetV2.referenced_tweets?.some((ref) => ref.type === "quoted") ?? false,
825
- inReplyToStatusId: tweetV2.referenced_tweets?.find((ref) => ref.type === "replied_to")?.id,
826
- quotedStatusId: tweetV2.referenced_tweets?.find((ref) => ref.type === "quoted")?.id,
827
- retweetedStatusId: tweetV2.referenced_tweets?.find((ref) => ref.type === "retweeted")?.id
830
+ inReplyToStatusId: tweetV2.referenced_tweets?.find(
831
+ (ref) => ref.type === "replied_to"
832
+ )?.id,
833
+ quotedStatusId: tweetV2.referenced_tweets?.find(
834
+ (ref) => ref.type === "quoted"
835
+ )?.id,
836
+ retweetedStatusId: tweetV2.referenced_tweets?.find(
837
+ (ref) => ref.type === "retweeted"
838
+ )?.id
828
839
  };
829
840
  if (includes?.polls?.length) {
830
841
  const poll = includes.polls[0];
@@ -976,7 +987,12 @@ async function* getTweets(user, maxTweets, auth) {
976
987
  let cursor;
977
988
  let totalFetched = 0;
978
989
  while (totalFetched < maxTweets) {
979
- const response = await fetchTweets(userId, maxTweets - totalFetched, cursor, auth);
990
+ const response = await fetchTweets(
991
+ userId,
992
+ maxTweets - totalFetched,
993
+ cursor,
994
+ auth
995
+ );
980
996
  for (const tweet of response.tweets) {
981
997
  yield tweet;
982
998
  totalFetched++;
@@ -990,7 +1006,12 @@ async function* getTweetsByUserId(userId, maxTweets, auth) {
990
1006
  let cursor;
991
1007
  let totalFetched = 0;
992
1008
  while (totalFetched < maxTweets) {
993
- const response = await fetchTweets(userId, maxTweets - totalFetched, cursor, auth);
1009
+ const response = await fetchTweets(
1010
+ userId,
1011
+ maxTweets - totalFetched,
1012
+ cursor,
1013
+ auth
1014
+ );
994
1015
  for (const tweet of response.tweets) {
995
1016
  yield tweet;
996
1017
  totalFetched++;
@@ -1009,7 +1030,12 @@ async function* getTweetsAndReplies(user, maxTweets, auth) {
1009
1030
  let cursor;
1010
1031
  let totalFetched = 0;
1011
1032
  while (totalFetched < maxTweets) {
1012
- const response = await fetchTweetsAndReplies(userId, maxTweets - totalFetched, cursor, auth);
1033
+ const response = await fetchTweetsAndReplies(
1034
+ userId,
1035
+ maxTweets - totalFetched,
1036
+ cursor,
1037
+ auth
1038
+ );
1013
1039
  for (const tweet of response.tweets) {
1014
1040
  yield tweet;
1015
1041
  totalFetched++;
@@ -1023,7 +1049,12 @@ async function* getTweetsAndRepliesByUserId(userId, maxTweets, auth) {
1023
1049
  let cursor;
1024
1050
  let totalFetched = 0;
1025
1051
  while (totalFetched < maxTweets) {
1026
- const response = await fetchTweetsAndReplies(userId, maxTweets - totalFetched, cursor, auth);
1052
+ const response = await fetchTweetsAndReplies(
1053
+ userId,
1054
+ maxTweets - totalFetched,
1055
+ cursor,
1056
+ auth
1057
+ );
1027
1058
  for (const tweet of response.tweets) {
1028
1059
  yield tweet;
1029
1060
  totalFetched++;
@@ -1115,10 +1146,7 @@ async function getTweetV2(id, auth, options = defaultOptions) {
1115
1146
  console.warn(`Tweet data not found for ID: ${id}`);
1116
1147
  return null;
1117
1148
  }
1118
- const parsedTweet = parseTweetV2ToV1(
1119
- tweetData.data,
1120
- tweetData?.includes
1121
- );
1149
+ const parsedTweet = parseTweetV2ToV1(tweetData.data, tweetData?.includes);
1122
1150
  return parsedTweet;
1123
1151
  } catch (error) {
1124
1152
  console.error(`Error fetching tweet ${id}:`, error);
@@ -6098,18 +6126,49 @@ async function validateTwitterConfig(runtime, config = {}) {
6098
6126
  TWITTER_API_SECRET_KEY: config.TWITTER_API_SECRET_KEY ?? getSetting(runtime, "TWITTER_API_SECRET_KEY") ?? "",
6099
6127
  TWITTER_ACCESS_TOKEN: config.TWITTER_ACCESS_TOKEN ?? getSetting(runtime, "TWITTER_ACCESS_TOKEN") ?? "",
6100
6128
  TWITTER_ACCESS_TOKEN_SECRET: config.TWITTER_ACCESS_TOKEN_SECRET ?? getSetting(runtime, "TWITTER_ACCESS_TOKEN_SECRET") ?? "",
6101
- TWITTER_DRY_RUN: String((config.TWITTER_DRY_RUN ?? getSetting(runtime, "TWITTER_DRY_RUN") ?? "false").toLowerCase() === "true"),
6129
+ TWITTER_DRY_RUN: String(
6130
+ (config.TWITTER_DRY_RUN ?? getSetting(runtime, "TWITTER_DRY_RUN") ?? "false").toLowerCase() === "true"
6131
+ ),
6102
6132
  TWITTER_TARGET_USERS: config.TWITTER_TARGET_USERS ?? getSetting(runtime, "TWITTER_TARGET_USERS") ?? "",
6103
- TWITTER_ENABLE_POST: String((config.TWITTER_ENABLE_POST ?? getSetting(runtime, "TWITTER_ENABLE_POST") ?? "false").toLowerCase() === "true"),
6133
+ TWITTER_ENABLE_POST: String(
6134
+ (config.TWITTER_ENABLE_POST ?? getSetting(runtime, "TWITTER_ENABLE_POST") ?? "false").toLowerCase() === "true"
6135
+ ),
6104
6136
  TWITTER_ENABLE_REPLIES: String(
6105
6137
  config.TWITTER_ENABLE_REPLIES !== void 0 ? config.TWITTER_ENABLE_REPLIES.toLowerCase() === "true" : (getSetting(runtime, "TWITTER_ENABLE_REPLIES") ?? "true").toLowerCase() === "true"
6106
6138
  ),
6107
- TWITTER_ENABLE_ACTIONS: String((config.TWITTER_ENABLE_ACTIONS ?? getSetting(runtime, "TWITTER_ENABLE_ACTIONS") ?? "false").toLowerCase() === "true"),
6108
- TWITTER_POST_INTERVAL: String(safeParseInt(config.TWITTER_POST_INTERVAL ?? getSetting(runtime, "TWITTER_POST_INTERVAL"), 120)),
6109
- TWITTER_ENGAGEMENT_INTERVAL: String(safeParseInt(config.TWITTER_ENGAGEMENT_INTERVAL ?? getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL"), 30)),
6110
- TWITTER_MAX_ENGAGEMENTS_PER_RUN: String(safeParseInt(config.TWITTER_MAX_ENGAGEMENTS_PER_RUN ?? getSetting(runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN"), 10)),
6111
- TWITTER_MAX_TWEET_LENGTH: String(safeParseInt(config.TWITTER_MAX_TWEET_LENGTH ?? getSetting(runtime, "TWITTER_MAX_TWEET_LENGTH"), 280)),
6112
- TWITTER_RETRY_LIMIT: String(safeParseInt(config.TWITTER_RETRY_LIMIT ?? getSetting(runtime, "TWITTER_RETRY_LIMIT"), 5))
6139
+ TWITTER_ENABLE_ACTIONS: String(
6140
+ (config.TWITTER_ENABLE_ACTIONS ?? getSetting(runtime, "TWITTER_ENABLE_ACTIONS") ?? "false").toLowerCase() === "true"
6141
+ ),
6142
+ TWITTER_POST_INTERVAL: String(
6143
+ safeParseInt(
6144
+ config.TWITTER_POST_INTERVAL ?? getSetting(runtime, "TWITTER_POST_INTERVAL"),
6145
+ 120
6146
+ )
6147
+ ),
6148
+ TWITTER_ENGAGEMENT_INTERVAL: String(
6149
+ safeParseInt(
6150
+ config.TWITTER_ENGAGEMENT_INTERVAL ?? getSetting(runtime, "TWITTER_ENGAGEMENT_INTERVAL"),
6151
+ 30
6152
+ )
6153
+ ),
6154
+ TWITTER_MAX_ENGAGEMENTS_PER_RUN: String(
6155
+ safeParseInt(
6156
+ config.TWITTER_MAX_ENGAGEMENTS_PER_RUN ?? getSetting(runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN"),
6157
+ 10
6158
+ )
6159
+ ),
6160
+ TWITTER_MAX_TWEET_LENGTH: String(
6161
+ safeParseInt(
6162
+ config.TWITTER_MAX_TWEET_LENGTH ?? getSetting(runtime, "TWITTER_MAX_TWEET_LENGTH"),
6163
+ 280
6164
+ )
6165
+ ),
6166
+ TWITTER_RETRY_LIMIT: String(
6167
+ safeParseInt(
6168
+ config.TWITTER_RETRY_LIMIT ?? getSetting(runtime, "TWITTER_RETRY_LIMIT"),
6169
+ 5
6170
+ )
6171
+ )
6113
6172
  };
6114
6173
  if (!validatedConfig.TWITTER_API_KEY || !validatedConfig.TWITTER_API_SECRET_KEY || !validatedConfig.TWITTER_ACCESS_TOKEN || !validatedConfig.TWITTER_ACCESS_TOKEN_SECRET) {
6115
6174
  throw new Error(
@@ -6161,7 +6220,9 @@ var TwitterInteractionClient = class {
6161
6220
  this.state?.TWITTER_ENGAGEMENT_INTERVAL || getSetting(this.runtime, "TWITTER_ENGAGEMENT_INTERVAL") || process.env.TWITTER_ENGAGEMENT_INTERVAL || "30"
6162
6221
  );
6163
6222
  const interactionInterval = engagementIntervalMinutes * 60 * 1e3;
6164
- logger3.info(`Twitter interaction client will check every ${engagementIntervalMinutes} minutes`);
6223
+ logger3.info(
6224
+ `Twitter interaction client will check every ${engagementIntervalMinutes} minutes`
6225
+ );
6165
6226
  this.handleTwitterInteractions();
6166
6227
  if (this.isRunning) {
6167
6228
  setTimeout(handleTwitterInteractionsLoop, interactionInterval);
@@ -6230,7 +6291,9 @@ var TwitterInteractionClient = class {
6230
6291
  if (targetUsers.length === 0 && !targetUsersConfig.includes("*")) {
6231
6292
  return;
6232
6293
  }
6233
- logger3.info(`Checking posts from target users: ${targetUsers.join(", ") || "everyone (*)"}`);
6294
+ logger3.info(
6295
+ `Checking posts from target users: ${targetUsers.join(", ") || "everyone (*)"}`
6296
+ );
6234
6297
  for (const targetUser of targetUsers) {
6235
6298
  try {
6236
6299
  const normalizedUsername = targetUser.replace(/^@/, "");
@@ -6242,8 +6305,13 @@ var TwitterInteractionClient = class {
6242
6305
  1 /* Latest */
6243
6306
  );
6244
6307
  if (searchResult.tweets.length > 0) {
6245
- logger3.info(`Found ${searchResult.tweets.length} posts from @${normalizedUsername}`);
6246
- await this.processTargetUserTweets(searchResult.tweets, normalizedUsername);
6308
+ logger3.info(
6309
+ `Found ${searchResult.tweets.length} posts from @${normalizedUsername}`
6310
+ );
6311
+ await this.processTargetUserTweets(
6312
+ searchResult.tweets,
6313
+ normalizedUsername
6314
+ );
6247
6315
  }
6248
6316
  } catch (error) {
6249
6317
  logger3.error(`Error searching posts from @${targetUser}:`, error);
@@ -6281,7 +6349,9 @@ var TwitterInteractionClient = class {
6281
6349
  }
6282
6350
  const shouldEngage = await this.shouldEngageWithTweet(tweet);
6283
6351
  if (shouldEngage) {
6284
- logger3.info(`Engaging with tweet from @${username}: ${tweet.text.substring(0, 50)}...`);
6352
+ logger3.info(
6353
+ `Engaging with tweet from @${username}: ${tweet.text.substring(0, 50)}...`
6354
+ );
6285
6355
  await this.ensureTweetContext(tweet);
6286
6356
  const engaged = await this.engageWithTweet(tweet);
6287
6357
  if (engaged) {
@@ -6305,7 +6375,9 @@ var TwitterInteractionClient = class {
6305
6375
  return tweetAge < 12 * 60 * 60 * 1e3;
6306
6376
  });
6307
6377
  if (relevantTweets.length > 0) {
6308
- logger3.info(`Found ${relevantTweets.length} relevant tweets from timeline`);
6378
+ logger3.info(
6379
+ `Found ${relevantTweets.length} relevant tweets from timeline`
6380
+ );
6309
6381
  await this.processTargetUserTweets(relevantTweets, "timeline");
6310
6382
  }
6311
6383
  } catch (error) {
@@ -6476,8 +6548,13 @@ Response (YES/NO):`;
6476
6548
  const maxInteractionsPerRun = parseInt(
6477
6549
  getSetting(this.runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN") || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
6478
6550
  );
6479
- const tweetsToProcess = uniqueTweetCandidates.slice(0, maxInteractionsPerRun);
6480
- logger3.info(`Processing ${tweetsToProcess.length} of ${uniqueTweetCandidates.length} mention tweets (max: ${maxInteractionsPerRun})`);
6551
+ const tweetsToProcess = uniqueTweetCandidates.slice(
6552
+ 0,
6553
+ maxInteractionsPerRun
6554
+ );
6555
+ logger3.info(
6556
+ `Processing ${tweetsToProcess.length} of ${uniqueTweetCandidates.length} mention tweets (max: ${maxInteractionsPerRun})`
6557
+ );
6481
6558
  for (const tweet of tweetsToProcess) {
6482
6559
  if (!this.client.lastCheckedTweetId || BigInt(tweet.id) > this.client.lastCheckedTweetId) {
6483
6560
  const tweetId = createUniqueUuid2(this.runtime, tweet.id);
@@ -6819,7 +6896,8 @@ Response (YES/NO):`;
6819
6896
  import {
6820
6897
  ChannelType as ChannelType2,
6821
6898
  createUniqueUuid as createUniqueUuid3,
6822
- logger as logger4
6899
+ logger as logger4,
6900
+ ModelType as ModelType2
6823
6901
  } from "@elizaos/core";
6824
6902
  var TwitterPostClient = class {
6825
6903
  /**
@@ -6871,11 +6949,11 @@ var TwitterPostClient = class {
6871
6949
  }
6872
6950
  };
6873
6951
  await new Promise((resolve) => setTimeout(resolve, 1e3));
6874
- const postImmediately = parseInt(
6875
- this.state?.TWITTER_POST_INTERVAL || getSetting(this.runtime, "TWITTER_POST_INTERVAL") || process.env.TWITTER_POST_INTERVAL || "120"
6876
- ) === 0;
6877
- if (postImmediately) {
6878
- logger4.info("TWITTER_POST_IMMEDIATELY is true, generating initial tweet now");
6952
+ const postImmediately = this.state?.TWITTER_POST_IMMEDIATELY || getSetting(this.runtime, "TWITTER_POST_IMMEDIATELY") || process.env.TWITTER_POST_IMMEDIATELY;
6953
+ if (postImmediately === "true" || postImmediately === true) {
6954
+ logger4.info(
6955
+ "TWITTER_POST_IMMEDIATELY is true, generating initial tweet now"
6956
+ );
6879
6957
  await this.generateNewTweet();
6880
6958
  }
6881
6959
  generateNewTweetLoop();
@@ -6892,70 +6970,85 @@ var TwitterPostClient = class {
6892
6970
  logger4.error("Cannot generate tweet: Twitter profile not available");
6893
6971
  return;
6894
6972
  }
6895
- logger4.info(`Generating tweet for user: ${this.client.profile?.username} (${userId})`);
6973
+ logger4.info(
6974
+ `Generating tweet for user: ${this.client.profile?.username} (${userId})`
6975
+ );
6896
6976
  const worldId = createUniqueUuid3(this.runtime, userId);
6897
6977
  const roomId = createUniqueUuid3(this.runtime, `${userId}-home`);
6898
- const callback = async (content) => {
6899
- logger4.info("Tweet generation callback triggered");
6900
- try {
6901
- if (this.isDryRun) {
6902
- logger4.info(`[DRY RUN] Would post tweet: ${content.text}`);
6903
- return [];
6904
- }
6905
- if (content.text.includes("Error: Missing")) {
6906
- logger4.error("Error: Missing some context", content);
6907
- return [];
6908
- }
6909
- logger4.info(`Posting tweet: ${content.text}`);
6910
- const result = await this.postToTwitter(
6911
- content.text,
6912
- content.mediaData
6913
- );
6914
- if (result === null) {
6915
- logger4.info("Skipped posting duplicate tweet");
6916
- return [];
6917
- }
6918
- const tweetId = result.id;
6919
- logger4.info(`Tweet posted successfully! ID: ${tweetId}`);
6920
- if (result) {
6921
- const postedTweetId = createUniqueUuid3(this.runtime, tweetId);
6922
- const postedMemory = {
6923
- id: postedTweetId,
6924
- entityId: this.runtime.agentId,
6925
- agentId: this.runtime.agentId,
6926
- roomId,
6927
- content: {
6928
- ...content,
6929
- source: "twitter",
6930
- channelType: ChannelType2.FEED,
6931
- type: "post",
6932
- metadata: {
6933
- tweetId,
6934
- postedAt: Date.now()
6935
- }
6936
- },
6937
- createdAt: Date.now()
6938
- };
6939
- await this.runtime.createMemory(postedMemory, "messages");
6940
- return [postedMemory];
6941
- }
6942
- return [];
6943
- } catch (error) {
6944
- logger4.error("Error in tweet generation callback:", error);
6945
- return [];
6946
- }
6947
- };
6948
- this.runtime.emitEvent(
6949
- "TWITTER_POST_GENERATED" /* POST_GENERATED */,
6978
+ const state = await this.runtime.composeState({
6979
+ agentId: this.runtime.agentId,
6980
+ entityId: this.runtime.agentId,
6981
+ roomId,
6982
+ content: { text: "", type: "post" },
6983
+ createdAt: Date.now()
6984
+ });
6985
+ const tweetPrompt = `You are ${this.runtime.character.name}.
6986
+ ${this.runtime.character.bio}
6987
+
6988
+ Generate a tweet that:
6989
+ - Reflects your personality and interests
6990
+ - Is engaging and authentic
6991
+ - Is between 50-280 characters
6992
+ - Does NOT include hashtags unless they're essential to the message
6993
+ - Does NOT include @mentions unless replying to someone
6994
+
6995
+ Your topics of interest: ${this.runtime.character.topics?.join(", ") || "general topics"}
6996
+
6997
+ Recent context:
6998
+ ${state.recentMemories?.slice(0, 5).map((m) => m.content.text).join("\n") || "No recent context"}
6999
+
7000
+ Generate a single tweet:`;
7001
+ const generatedContent = await this.runtime.useModel(
7002
+ ModelType2.TEXT_SMALL,
6950
7003
  {
6951
- callback,
6952
- entityId: this.runtime.agentId,
6953
- userId,
6954
- roomId,
6955
- source: "twitter"
7004
+ prompt: tweetPrompt,
7005
+ temperature: 0.8,
7006
+ maxTokens: 100
6956
7007
  }
6957
7008
  );
6958
- logger4.info("POST_GENERATED event emitted successfully");
7009
+ const tweetText = generatedContent.trim();
7010
+ if (!tweetText || tweetText.length === 0) {
7011
+ logger4.error("Generated empty tweet content");
7012
+ return;
7013
+ }
7014
+ if (tweetText.includes("Error: Missing")) {
7015
+ logger4.error("Error in generated content:", tweetText);
7016
+ return;
7017
+ }
7018
+ logger4.info(`Generated tweet: ${tweetText}`);
7019
+ if (this.isDryRun) {
7020
+ logger4.info(`[DRY RUN] Would post tweet: ${tweetText}`);
7021
+ return;
7022
+ }
7023
+ const result = await this.postToTwitter(tweetText, []);
7024
+ if (result === null) {
7025
+ logger4.info("Skipped posting duplicate tweet");
7026
+ return;
7027
+ }
7028
+ const tweetId = result.id;
7029
+ logger4.info(`Tweet posted successfully! ID: ${tweetId}`);
7030
+ if (result) {
7031
+ const postedTweetId = createUniqueUuid3(this.runtime, tweetId);
7032
+ const postedMemory = {
7033
+ id: postedTweetId,
7034
+ entityId: this.runtime.agentId,
7035
+ agentId: this.runtime.agentId,
7036
+ roomId,
7037
+ content: {
7038
+ text: tweetText,
7039
+ source: "twitter",
7040
+ channelType: ChannelType2.FEED,
7041
+ type: "post",
7042
+ metadata: {
7043
+ tweetId,
7044
+ postedAt: Date.now()
7045
+ }
7046
+ },
7047
+ createdAt: Date.now()
7048
+ };
7049
+ await this.runtime.createMemory(postedMemory, "messages");
7050
+ logger4.info("Tweet posted and saved to memory successfully");
7051
+ }
6959
7052
  } catch (error) {
6960
7053
  logger4.error("Error generating tweet:", error);
6961
7054
  }
@@ -7014,7 +7107,7 @@ import {
7014
7107
  ChannelType as ChannelType3,
7015
7108
  composePromptFromState,
7016
7109
  createUniqueUuid as createUniqueUuid4,
7017
- ModelType as ModelType2,
7110
+ ModelType as ModelType3,
7018
7111
  parseKeyValueXml
7019
7112
  } from "@elizaos/core";
7020
7113
  import { logger as logger5 } from "@elizaos/core";
@@ -7026,20 +7119,23 @@ var twitterActionTemplate = `
7026
7119
  {{postDirections}}
7027
7120
 
7028
7121
  Guidelines:
7029
- - ONLY engage with content that DIRECTLY relates to character's core interests
7030
- - Direct mentions are priority IF they are on-topic
7031
- - Skip ALL content that is:
7032
- - Off-topic or tangentially related
7033
- - From high-profile accounts unless explicitly relevant
7034
- - Generic/viral content without specific relevance
7035
- - Political/controversial unless central to character
7036
- - Promotional/marketing unless directly relevant
7122
+ - Engage with content that relates to character's interests and expertise
7123
+ - Direct mentions should be prioritized when relevant
7124
+ - Consider engaging with:
7125
+ - Content directly related to your topics
7126
+ - Interesting discussions you can contribute to
7127
+ - Questions you can help answer
7128
+ - Content from users you've interacted with before
7129
+ - Skip content that is:
7130
+ - Completely off-topic or spam
7131
+ - Inflammatory or highly controversial (unless it's your area)
7132
+ - Pure marketing/promotional with no value
7037
7133
 
7038
7134
  Actions (respond only with tags):
7039
- [LIKE] - Perfect topic match AND aligns with character (9.8/10)
7040
- [RETWEET] - Exceptional content that embodies character's expertise (9.5/10)
7041
- [QUOTE] - Can add substantial domain expertise (9.5/10)
7042
- [REPLY] - Can contribute meaningful, expert-level insight (9.5/10)
7135
+ [LIKE] - Content is relevant and interesting (7/10 or higher)
7136
+ [RETWEET] - Content is valuable and worth sharing (8/10 or higher)
7137
+ [QUOTE] - You can add meaningful commentary (7.5/10 or higher)
7138
+ [REPLY] - You can contribute helpful insights (7/10 or higher)
7043
7139
  `;
7044
7140
  var quoteTweetTemplate = `# Task: Write a quote tweet in the voice, style, and perspective of {{agentName}} @{{twitterUserName}}.
7045
7141
 
@@ -7105,7 +7201,9 @@ var TwitterTimelineClient = class {
7105
7201
  this.state?.TWITTER_ENGAGEMENT_INTERVAL || getSetting(this.runtime, "TWITTER_ENGAGEMENT_INTERVAL") || process.env.TWITTER_ENGAGEMENT_INTERVAL || "30"
7106
7202
  );
7107
7203
  const actionInterval = engagementIntervalMinutes * 60 * 1e3;
7108
- logger5.info(`Timeline client will check every ${engagementIntervalMinutes} minutes`);
7204
+ logger5.info(
7205
+ `Timeline client will check every ${engagementIntervalMinutes} minutes`
7206
+ );
7109
7207
  this.handleTimeline();
7110
7208
  if (this.isRunning) {
7111
7209
  setTimeout(handleTwitterTimelineLoop, actionInterval);
@@ -7173,7 +7271,7 @@ ${tweet.text}
7173
7271
 
7174
7272
  Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appropriate. Each action must be on its own line. Your response must only include the chosen actions.`;
7175
7273
  const actionResponse = await this.runtime.useModel(
7176
- ModelType2.TEXT_SMALL,
7274
+ ModelType3.TEXT_SMALL,
7177
7275
  {
7178
7276
  prompt: actionRespondPrompt
7179
7277
  }
@@ -7227,7 +7325,12 @@ ${actionSummary.join("\n")}`);
7227
7325
  }
7228
7326
  async processTimelineActions(tweetDecisions) {
7229
7327
  const results = [];
7230
- for (const { tweet, actionResponse, tweetState, roomId } of tweetDecisions) {
7328
+ for (const {
7329
+ tweet,
7330
+ actionResponse,
7331
+ tweetState,
7332
+ roomId
7333
+ } of tweetDecisions) {
7231
7334
  const tweetId = this.createTweetId(this.runtime, tweet);
7232
7335
  const executedActions = [];
7233
7336
  await this.runtime.createMemory(
@@ -7345,13 +7448,15 @@ ${actionSummary.join("\n")}`);
7345
7448
  }) + `
7346
7449
  You are responding to this tweet:
7347
7450
  ${tweet.text}`;
7348
- const quoteResponse = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
7451
+ const quoteResponse = await this.runtime.useModel(ModelType3.TEXT_SMALL, {
7349
7452
  prompt: quotePrompt
7350
7453
  });
7351
7454
  const responseObject = parseKeyValueXml(quoteResponse);
7352
7455
  if (responseObject.post) {
7353
7456
  if (this.isDryRun) {
7354
- logger5.log(`[DRY RUN] Would have quoted tweet ${tweet.id} with: ${responseObject.post}`);
7457
+ logger5.log(
7458
+ `[DRY RUN] Would have quoted tweet ${tweet.id} with: ${responseObject.post}`
7459
+ );
7355
7460
  return;
7356
7461
  }
7357
7462
  const result = await this.client.requestQueue.add(
@@ -7396,13 +7501,15 @@ ${tweet.text}`;
7396
7501
  }) + `
7397
7502
  You are replying to this tweet:
7398
7503
  ${tweet.text}`;
7399
- const replyResponse = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
7504
+ const replyResponse = await this.runtime.useModel(ModelType3.TEXT_SMALL, {
7400
7505
  prompt: replyPrompt
7401
7506
  });
7402
7507
  const responseObject = parseKeyValueXml(replyResponse);
7403
7508
  if (responseObject.post) {
7404
7509
  if (this.isDryRun) {
7405
- logger5.log(`[DRY RUN] Would have replied to tweet ${tweet.id} with: ${responseObject.post}`);
7510
+ logger5.log(
7511
+ `[DRY RUN] Would have replied to tweet ${tweet.id} with: ${responseObject.post}`
7512
+ );
7406
7513
  return;
7407
7514
  }
7408
7515
  const result = await sendTweet(
@@ -7438,14 +7545,16 @@ ${tweet.text}`;
7438
7545
  import {
7439
7546
  createUniqueUuid as createUniqueUuid5,
7440
7547
  logger as logger6,
7441
- ModelType as ModelType3
7548
+ ModelType as ModelType4
7442
7549
  } from "@elizaos/core";
7443
7550
  var TwitterDiscoveryClient = class {
7444
7551
  constructor(client, runtime, state) {
7445
7552
  this.isRunning = false;
7553
+ this.lastDiscoveryTime = 0;
7446
7554
  this.client = client;
7447
7555
  this.twitterClient = client.twitterClient;
7448
7556
  this.runtime = runtime;
7557
+ this.state = state;
7449
7558
  const dryRunSetting = state?.TWITTER_DRY_RUN ?? getSetting(this.runtime, "TWITTER_DRY_RUN") ?? process.env.TWITTER_DRY_RUN;
7450
7559
  this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
7451
7560
  this.config = this.buildDiscoveryConfig();
@@ -7457,6 +7566,17 @@ var TwitterDiscoveryClient = class {
7457
7566
  maxEngagementsPerCycle: this.config.maxEngagementsPerCycle
7458
7567
  });
7459
7568
  }
7569
+ /**
7570
+ * Sanitizes a topic for use in Twitter search queries
7571
+ * - Removes common stop words that might be interpreted as operators
7572
+ * - Handles special characters
7573
+ * - Simplifies complex phrases
7574
+ */
7575
+ sanitizeTopic(topic) {
7576
+ let sanitized = topic.replace(/\band\b/gi, " ").replace(/\bor\b/gi, " ").replace(/\bnot\b/gi, " ").trim();
7577
+ sanitized = sanitized.replace(/\s+/g, " ");
7578
+ return sanitized.includes(" ") ? `"${sanitized}"` : sanitized;
7579
+ }
7460
7580
  buildDiscoveryConfig() {
7461
7581
  const character = this.runtime?.character;
7462
7582
  const defaultTopics = [
@@ -7476,7 +7596,9 @@ var TwitterDiscoveryClient = class {
7476
7596
  topics = this.extractTopicsFromBio(character.bio);
7477
7597
  }
7478
7598
  } else {
7479
- logger6.warn("Character not available in runtime, using default topics for discovery");
7599
+ logger6.warn(
7600
+ "Character not available in runtime, using default topics for discovery"
7601
+ );
7480
7602
  }
7481
7603
  return {
7482
7604
  topics,
@@ -7487,11 +7609,17 @@ var TwitterDiscoveryClient = class {
7487
7609
  getSetting(this.runtime, "TWITTER_MAX_FOLLOWS_PER_CYCLE") || process.env.TWITTER_MAX_FOLLOWS_PER_CYCLE || "5"
7488
7610
  ),
7489
7611
  maxEngagementsPerCycle: parseInt(
7490
- getSetting(this.runtime, "TWITTER_MAX_ENGAGEMENTS_PER_RUN") || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
7612
+ getSetting(
7613
+ this.runtime,
7614
+ "TWITTER_MAX_ENGAGEMENTS_PER_RUN"
7615
+ ) || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
7491
7616
  ),
7492
- likeThreshold: 0.6,
7493
- replyThreshold: 0.8,
7494
- quoteThreshold: 0.85
7617
+ likeThreshold: 0.3,
7618
+ // Lowered from 0.6
7619
+ replyThreshold: 0.5,
7620
+ // Lowered from 0.8
7621
+ quoteThreshold: 0.7
7622
+ // Lowered from 0.85
7495
7623
  };
7496
7624
  }
7497
7625
  extractTopicsFromBio(bio) {
@@ -7499,7 +7627,16 @@ var TwitterDiscoveryClient = class {
7499
7627
  return [];
7500
7628
  }
7501
7629
  const bioText = Array.isArray(bio) ? bio.join(" ") : bio;
7502
- const words = bioText.toLowerCase().split(/\s+/).filter((word) => word.length > 4).filter((word) => !["about", "helping", "working", "people", "making", "building"].includes(word));
7630
+ const words = bioText.toLowerCase().split(/\s+/).filter((word) => word.length > 4).filter(
7631
+ (word) => ![
7632
+ "about",
7633
+ "helping",
7634
+ "working",
7635
+ "people",
7636
+ "making",
7637
+ "building"
7638
+ ].includes(word)
7639
+ );
7503
7640
  return [...new Set(words)].slice(0, 5);
7504
7641
  }
7505
7642
  async start() {
@@ -7520,7 +7657,9 @@ var TwitterDiscoveryClient = class {
7520
7657
  );
7521
7658
  const variance = Math.random() * 20 - 10;
7522
7659
  const nextInterval = (baseInterval + variance) * 60 * 1e3;
7523
- logger6.info(`Next discovery cycle in ${(baseInterval + variance).toFixed(1)} minutes`);
7660
+ logger6.info(
7661
+ `Next discovery cycle in ${(baseInterval + variance).toFixed(1)} minutes`
7662
+ );
7524
7663
  if (this.isRunning) {
7525
7664
  setTimeout(discoveryLoop, nextInterval);
7526
7665
  }
@@ -7535,7 +7674,9 @@ var TwitterDiscoveryClient = class {
7535
7674
  logger6.info("Starting Twitter discovery cycle...");
7536
7675
  const discoveries = await this.discoverContent();
7537
7676
  const { tweets, accounts } = discoveries;
7538
- logger6.info(`Discovered ${tweets.length} tweets and ${accounts.length} accounts`);
7677
+ logger6.info(
7678
+ `Discovered ${tweets.length} tweets and ${accounts.length} accounts`
7679
+ );
7539
7680
  const followedCount = await this.processAccounts(accounts);
7540
7681
  const engagementCount = await this.processTweets(tweets);
7541
7682
  logger6.info(
@@ -7548,9 +7689,7 @@ var TwitterDiscoveryClient = class {
7548
7689
  try {
7549
7690
  const topicContent = await this.discoverFromTopics();
7550
7691
  allTweets.push(...topicContent.tweets);
7551
- topicContent.accounts.forEach(
7552
- (acc) => allAccounts.set(acc.user.id, acc)
7553
- );
7692
+ topicContent.accounts.forEach((acc) => allAccounts.set(acc.user.id, acc));
7554
7693
  } catch (error) {
7555
7694
  logger6.error("Failed to discover from topics:", error);
7556
7695
  }
@@ -7573,7 +7712,9 @@ var TwitterDiscoveryClient = class {
7573
7712
  logger6.error("Failed to discover from popular accounts:", error);
7574
7713
  }
7575
7714
  const sortedTweets = allTweets.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, 50);
7576
- const sortedAccounts = Array.from(allAccounts.values()).sort((a, b) => b.qualityScore * b.relevanceScore - a.qualityScore * a.relevanceScore).slice(0, 20);
7715
+ const sortedAccounts = Array.from(allAccounts.values()).sort(
7716
+ (a, b) => b.qualityScore * b.relevanceScore - a.qualityScore * a.relevanceScore
7717
+ ).slice(0, 20);
7577
7718
  return { tweets: sortedTweets, accounts: sortedAccounts };
7578
7719
  }
7579
7720
  async discoverFromTopics() {
@@ -7582,7 +7723,8 @@ var TwitterDiscoveryClient = class {
7582
7723
  const accounts = /* @__PURE__ */ new Map();
7583
7724
  for (const topic of this.config.topics.slice(0, 5)) {
7584
7725
  try {
7585
- const popularQuery = `${topic} -is:retweet -is:reply lang:en`;
7726
+ const searchTopic = this.sanitizeTopic(topic);
7727
+ const popularQuery = `${searchTopic} -is:retweet -is:reply lang:en`;
7586
7728
  logger6.debug(`Searching popular tweets for topic: ${topic}`);
7587
7729
  const popularResults = await this.twitterClient.fetchSearchTweets(
7588
7730
  popularQuery,
@@ -7594,7 +7736,7 @@ var TwitterDiscoveryClient = class {
7594
7736
  const scored = this.scoreTweet(tweet, "topic");
7595
7737
  tweets.push(scored);
7596
7738
  }
7597
- const verifiedQuery = `${topic} -is:retweet lang:en is:verified`;
7739
+ const verifiedQuery = `${searchTopic} -is:retweet lang:en is:verified`;
7598
7740
  logger6.debug(`Searching verified accounts for topic: ${topic}`);
7599
7741
  const verifiedResults = await this.twitterClient.fetchSearchTweets(
7600
7742
  verifiedQuery,
@@ -7627,9 +7769,9 @@ var TwitterDiscoveryClient = class {
7627
7769
  logger6.debug("Discovering from conversation threads...");
7628
7770
  const tweets = [];
7629
7771
  const accounts = /* @__PURE__ */ new Map();
7630
- const topicQuery = this.config.topics.slice(0, 3).map((t) => `"${t}"`).join(" OR ");
7772
+ const topicQuery = this.config.topics.slice(0, 3).map((t) => this.sanitizeTopic(t)).join(" OR ");
7631
7773
  try {
7632
- const viralQuery = `${topicQuery} -is:retweet has:mentions`;
7774
+ const viralQuery = `(${topicQuery}) -is:retweet has:mentions`;
7633
7775
  logger6.debug(`Searching viral threads with query: ${viralQuery}`);
7634
7776
  const searchResults = await this.twitterClient.fetchSearchTweets(
7635
7777
  viralQuery,
@@ -7638,17 +7780,17 @@ var TwitterDiscoveryClient = class {
7638
7780
  );
7639
7781
  for (const tweet of searchResults.tweets) {
7640
7782
  const engagementScore = (tweet.likes || 0) + (tweet.retweets || 0) * 2;
7641
- if (engagementScore < 50) continue;
7783
+ if (engagementScore < 10) continue;
7642
7784
  const scored = this.scoreTweet(tweet, "thread");
7643
7785
  tweets.push(scored);
7644
7786
  const account = this.scoreAccount({
7645
7787
  id: tweet.userId,
7646
7788
  username: tweet.username,
7647
7789
  name: tweet.name || tweet.username,
7648
- followersCount: 5e3
7649
- // Estimate for viral content creators
7790
+ followersCount: 1e3
7791
+ // Reasonable estimate for engaged users
7650
7792
  });
7651
- if (account.qualityScore > 0.6) {
7793
+ if (account.qualityScore > 0.5) {
7652
7794
  accounts.set(tweet.userId, account);
7653
7795
  }
7654
7796
  }
@@ -7663,7 +7805,8 @@ var TwitterDiscoveryClient = class {
7663
7805
  const accounts = /* @__PURE__ */ new Map();
7664
7806
  for (const topic of this.config.topics.slice(0, 3)) {
7665
7807
  try {
7666
- const influencerQuery = `${topic} -is:retweet (is:verified OR has:links)`;
7808
+ const searchTopic = this.sanitizeTopic(topic);
7809
+ const influencerQuery = `${searchTopic} -is:retweet lang:en`;
7667
7810
  logger6.debug(`Searching for influencers in topic: ${topic}`);
7668
7811
  const results = await this.twitterClient.fetchSearchTweets(
7669
7812
  influencerQuery,
@@ -7672,7 +7815,7 @@ var TwitterDiscoveryClient = class {
7672
7815
  );
7673
7816
  for (const tweet of results.tweets) {
7674
7817
  const engagement = (tweet.likes || 0) + (tweet.retweets || 0) * 2;
7675
- if (engagement < 20) continue;
7818
+ if (engagement < 5) continue;
7676
7819
  const scored = this.scoreTweet(tweet, "topic");
7677
7820
  tweets.push(scored);
7678
7821
  const estimatedFollowers = Math.max(
@@ -7691,7 +7834,10 @@ var TwitterDiscoveryClient = class {
7691
7834
  }
7692
7835
  }
7693
7836
  } catch (error) {
7694
- logger6.error(`Failed to discover popular accounts for ${topic}:`, error);
7837
+ logger6.error(
7838
+ `Failed to discover popular accounts for ${topic}:`,
7839
+ error
7840
+ );
7695
7841
  }
7696
7842
  }
7697
7843
  return { tweets, accounts: Array.from(accounts.values()) };
@@ -7701,12 +7847,15 @@ var TwitterDiscoveryClient = class {
7701
7847
  scoreTweet(tweet, source) {
7702
7848
  let relevanceScore = 0;
7703
7849
  const sourceScores = {
7704
- topic: 0.5,
7705
- thread: 0.4
7850
+ topic: 0.4,
7851
+ thread: 0.35
7706
7852
  };
7707
7853
  relevanceScore += sourceScores[source];
7708
7854
  const engagementScore = Math.min(
7709
- (tweet.likes || 0) / 1e3 + (tweet.retweets || 0) / 500 + (tweet.replies || 0) / 100,
7855
+ (tweet.likes || 0) / 100 + // 100 likes = 0.1 points (was 1000)
7856
+ (tweet.retweets || 0) / 50 + // 50 retweets = 0.1 points (was 500)
7857
+ (tweet.replies || 0) / 20,
7858
+ // 20 replies = 0.1 points (was 100)
7710
7859
  0.3
7711
7860
  );
7712
7861
  relevanceScore += engagementScore;
@@ -7714,7 +7863,7 @@ var TwitterDiscoveryClient = class {
7714
7863
  const topicMatches = this.config.topics.filter(
7715
7864
  (topic) => textLower.includes(topic.toLowerCase())
7716
7865
  ).length;
7717
- relevanceScore += Math.min(topicMatches * 0.1, 0.3);
7866
+ relevanceScore += Math.min(topicMatches * 0.15, 0.3);
7718
7867
  relevanceScore = Math.min(relevanceScore, 1);
7719
7868
  let engagementType = "skip";
7720
7869
  if (relevanceScore >= this.config.quoteThreshold) {
@@ -7768,7 +7917,10 @@ var TwitterDiscoveryClient = class {
7768
7917
  followedCount++;
7769
7918
  await this.delay(2e3 + Math.random() * 3e3);
7770
7919
  } catch (error) {
7771
- logger6.error(`Failed to follow @${scoredAccount.user.username}:`, error);
7920
+ logger6.error(
7921
+ `Failed to follow @${scoredAccount.user.username}:`,
7922
+ error
7923
+ );
7772
7924
  }
7773
7925
  }
7774
7926
  return followedCount;
@@ -7779,51 +7931,76 @@ var TwitterDiscoveryClient = class {
7779
7931
  if (engagementCount >= this.config.maxEngagementsPerCycle) break;
7780
7932
  if (scoredTweet.engagementType === "skip") continue;
7781
7933
  try {
7782
- const tweetMemoryId = createUniqueUuid5(this.runtime, scoredTweet.tweet.id);
7934
+ const tweetMemoryId = createUniqueUuid5(
7935
+ this.runtime,
7936
+ scoredTweet.tweet.id
7937
+ );
7783
7938
  const existingMemory = await this.runtime.getMemoryById(tweetMemoryId);
7784
7939
  if (existingMemory) {
7785
- logger6.debug(`Already engaged with tweet ${scoredTweet.tweet.id}, skipping`);
7940
+ logger6.debug(
7941
+ `Already engaged with tweet ${scoredTweet.tweet.id}, skipping`
7942
+ );
7786
7943
  continue;
7787
7944
  }
7788
7945
  switch (scoredTweet.engagementType) {
7789
7946
  case "like":
7790
7947
  if (this.isDryRun) {
7791
- logger6.info(`[DRY RUN] Would like tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`);
7948
+ logger6.info(
7949
+ `[DRY RUN] Would like tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
7950
+ );
7792
7951
  } else {
7793
7952
  await this.twitterClient.likeTweet(scoredTweet.tweet.id);
7794
- logger6.info(`Liked tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`);
7953
+ logger6.info(
7954
+ `Liked tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
7955
+ );
7795
7956
  }
7796
7957
  break;
7797
7958
  case "reply":
7798
7959
  const replyText = await this.generateReply(scoredTweet.tweet);
7799
7960
  if (this.isDryRun) {
7800
- logger6.info(`[DRY RUN] Would reply to tweet ${scoredTweet.tweet.id} with: "${replyText}"`);
7961
+ logger6.info(
7962
+ `[DRY RUN] Would reply to tweet ${scoredTweet.tweet.id} with: "${replyText}"`
7963
+ );
7801
7964
  } else {
7802
- await this.twitterClient.sendTweet(replyText, scoredTweet.tweet.id);
7965
+ await this.twitterClient.sendTweet(
7966
+ replyText,
7967
+ scoredTweet.tweet.id
7968
+ );
7803
7969
  logger6.info(`Replied to tweet: ${scoredTweet.tweet.id}`);
7804
7970
  }
7805
7971
  break;
7806
7972
  case "quote":
7807
7973
  const quoteText = await this.generateQuote(scoredTweet.tweet);
7808
7974
  if (this.isDryRun) {
7809
- logger6.info(`[DRY RUN] Would quote tweet ${scoredTweet.tweet.id} with: "${quoteText}"`);
7975
+ logger6.info(
7976
+ `[DRY RUN] Would quote tweet ${scoredTweet.tweet.id} with: "${quoteText}"`
7977
+ );
7810
7978
  } else {
7811
- await this.twitterClient.sendQuoteTweet(quoteText, scoredTweet.tweet.id);
7979
+ await this.twitterClient.sendQuoteTweet(
7980
+ quoteText,
7981
+ scoredTweet.tweet.id
7982
+ );
7812
7983
  logger6.info(`Quoted tweet: ${scoredTweet.tweet.id}`);
7813
7984
  }
7814
7985
  break;
7815
7986
  }
7816
- await this.saveEngagementMemory(scoredTweet.tweet, scoredTweet.engagementType);
7987
+ await this.saveEngagementMemory(
7988
+ scoredTweet.tweet,
7989
+ scoredTweet.engagementType
7990
+ );
7817
7991
  engagementCount++;
7818
7992
  await this.delay(3e3 + Math.random() * 5e3);
7819
7993
  } catch (error) {
7820
- logger6.error(`Failed to engage with tweet ${scoredTweet.tweet.id}:`, error);
7994
+ logger6.error(
7995
+ `Failed to engage with tweet ${scoredTweet.tweet.id}:`,
7996
+ error
7997
+ );
7821
7998
  }
7822
7999
  }
7823
8000
  return engagementCount;
7824
8001
  }
7825
8002
  async checkIfFollowing(userId) {
7826
- const embedding = await this.runtime.useModel(ModelType3.TEXT_EMBEDDING, {
8003
+ const embedding = await this.runtime.useModel(ModelType4.TEXT_EMBEDDING, {
7827
8004
  text: `followed twitter user ${userId}`
7828
8005
  });
7829
8006
  const followMemories = await this.runtime.searchMemories({
@@ -7859,7 +8036,7 @@ Keep the reply:
7859
8036
  - Respectful and constructive
7860
8037
 
7861
8038
  Reply:`;
7862
- const response = await this.runtime.useModel(ModelType3.TEXT_SMALL, {
8039
+ const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
7863
8040
  prompt,
7864
8041
  max_tokens: 100,
7865
8042
  temperature: 0.8
@@ -7891,7 +8068,7 @@ Create a quote tweet that:
7891
8068
  - Encourages further discussion
7892
8069
 
7893
8070
  Quote tweet:`;
7894
- const response = await this.runtime.useModel(ModelType3.TEXT_SMALL, {
8071
+ const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
7895
8072
  prompt,
7896
8073
  max_tokens: 100,
7897
8074
  temperature: 0.8
@@ -7899,37 +8076,43 @@ Quote tweet:`;
7899
8076
  return response.trim();
7900
8077
  }
7901
8078
  async saveEngagementMemory(tweet, engagementType) {
7902
- const memoryId = await this.runtime.createMemory({
7903
- id: createUniqueUuid5(this.runtime, tweet.id),
7904
- entityId: createUniqueUuid5(this.runtime, tweet.userId),
7905
- content: {
7906
- text: `${engagementType} tweet from @${tweet.username}: ${tweet.text}`,
7907
- metadata: {
7908
- tweetId: tweet.id,
7909
- engagementType,
7910
- source: "discovery",
7911
- isDryRun: this.isDryRun
7912
- }
8079
+ const memoryId = await this.runtime.createMemory(
8080
+ {
8081
+ id: createUniqueUuid5(this.runtime, tweet.id),
8082
+ entityId: createUniqueUuid5(this.runtime, tweet.userId),
8083
+ content: {
8084
+ text: `${engagementType} tweet from @${tweet.username}: ${tweet.text}`,
8085
+ metadata: {
8086
+ tweetId: tweet.id,
8087
+ engagementType,
8088
+ source: "discovery",
8089
+ isDryRun: this.isDryRun
8090
+ }
8091
+ },
8092
+ roomId: createUniqueUuid5(this.runtime, tweet.conversationId)
7913
8093
  },
7914
- roomId: createUniqueUuid5(this.runtime, tweet.conversationId)
7915
- }, "messages");
8094
+ "messages"
8095
+ );
7916
8096
  }
7917
8097
  async saveFollowMemory(user) {
7918
- const memoryId = await this.runtime.createMemory({
7919
- entityId: createUniqueUuid5(this.runtime, user.id),
7920
- content: {
7921
- text: `followed twitter user ${user.id} @${user.username}`,
7922
- metadata: {
7923
- userId: user.id,
7924
- username: user.username,
7925
- name: user.name,
7926
- followersCount: user.followersCount,
7927
- source: "discovery",
7928
- isDryRun: this.isDryRun
7929
- }
8098
+ const memoryId = await this.runtime.createMemory(
8099
+ {
8100
+ entityId: createUniqueUuid5(this.runtime, user.id),
8101
+ content: {
8102
+ text: `followed twitter user ${user.id} @${user.username}`,
8103
+ metadata: {
8104
+ userId: user.id,
8105
+ username: user.username,
8106
+ name: user.name,
8107
+ followersCount: user.followersCount,
8108
+ source: "discovery",
8109
+ isDryRun: this.isDryRun
8110
+ }
8111
+ },
8112
+ roomId: createUniqueUuid5(this.runtime, `twitter-follows`)
7930
8113
  },
7931
- roomId: createUniqueUuid5(this.runtime, `twitter-follows`)
7932
- }, "messages");
8114
+ "messages"
8115
+ );
7933
8116
  }
7934
8117
  delay(ms) {
7935
8118
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -7993,7 +8176,9 @@ var RequestQueue = class {
7993
8176
  await this.exponentialBackoff(retryCount);
7994
8177
  break;
7995
8178
  } else {
7996
- logger7.error(`Max retries (${this.maxRetries}) exceeded for request, skipping`);
8179
+ logger7.error(
8180
+ `Max retries (${this.maxRetries}) exceeded for request, skipping`
8181
+ );
7997
8182
  this.retryAttempts.delete(request);
7998
8183
  }
7999
8184
  }
@@ -8526,13 +8711,17 @@ var TwitterClientInstance = class {
8526
8711
  constructor(runtime, state) {
8527
8712
  this.client = new ClientBase(runtime, state);
8528
8713
  const postEnabledSetting = getSetting(runtime, "TWITTER_ENABLE_POST") ?? process.env.TWITTER_ENABLE_POST;
8529
- logger8.debug(`TWITTER_ENABLE_POST setting value: ${JSON.stringify(postEnabledSetting)}, type: ${typeof postEnabledSetting}`);
8714
+ logger8.debug(
8715
+ `TWITTER_ENABLE_POST setting value: ${JSON.stringify(postEnabledSetting)}, type: ${typeof postEnabledSetting}`
8716
+ );
8530
8717
  const postEnabled = postEnabledSetting === "true" || postEnabledSetting === true;
8531
8718
  if (postEnabled) {
8532
8719
  logger8.info("Twitter posting is ENABLED - creating post client");
8533
8720
  this.post = new TwitterPostClient(this.client, runtime, state);
8534
8721
  } else {
8535
- logger8.info("Twitter posting is DISABLED - set TWITTER_ENABLE_POST=true to enable automatic posting");
8722
+ logger8.info(
8723
+ "Twitter posting is DISABLED - set TWITTER_ENABLE_POST=true to enable automatic posting"
8724
+ );
8536
8725
  }
8537
8726
  const repliesEnabled = (getSetting(runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
8538
8727
  if (repliesEnabled) {
@@ -8557,7 +8746,9 @@ var TwitterClientInstance = class {
8557
8746
  logger8.info("Twitter discovery service is ENABLED");
8558
8747
  this.discovery = new TwitterDiscoveryClient(this.client, runtime, state);
8559
8748
  } else {
8560
- logger8.info("Twitter discovery service is DISABLED - set TWITTER_ENABLE_DISCOVERY=true to enable");
8749
+ logger8.info(
8750
+ "Twitter discovery service is DISABLED - set TWITTER_ENABLE_DISCOVERY=true to enable"
8751
+ );
8561
8752
  }
8562
8753
  }
8563
8754
  };
@@ -8620,11 +8811,17 @@ var TwitterService = _TwitterService;
8620
8811
  // src/actions/postTweet.ts
8621
8812
  import {
8622
8813
  logger as logger9,
8623
- ModelType as ModelType4
8814
+ ModelType as ModelType5
8624
8815
  } from "@elizaos/core";
8625
8816
  var postTweetAction = {
8626
8817
  name: "POST_TWEET",
8627
- similes: ["TWEET", "SEND_TWEET", "TWITTER_POST", "POST_ON_TWITTER", "SHARE_ON_TWITTER"],
8818
+ similes: [
8819
+ "TWEET",
8820
+ "SEND_TWEET",
8821
+ "TWITTER_POST",
8822
+ "POST_ON_TWITTER",
8823
+ "SHARE_ON_TWITTER"
8824
+ ],
8628
8825
  validate: async (runtime, message) => {
8629
8826
  logger9.debug("Validating POST_TWEET action");
8630
8827
  const text = message.content?.text?.trim();
@@ -8646,7 +8843,9 @@ var postTweetAction = {
8646
8843
  await client.init();
8647
8844
  }
8648
8845
  if (!client.profile) {
8649
- throw new Error("Twitter client not properly initialized - no profile found");
8846
+ throw new Error(
8847
+ "Twitter client not properly initialized - no profile found"
8848
+ );
8650
8849
  }
8651
8850
  const tweetText = message.content?.text?.trim() || "";
8652
8851
  let finalTweetText = tweetText;
@@ -8667,7 +8866,7 @@ Generate a tweet that:
8667
8866
  - Is not generic or promotional
8668
8867
 
8669
8868
  Tweet:`;
8670
- const response = await runtime.useModel(ModelType4.TEXT_SMALL, {
8869
+ const response = await runtime.useModel(ModelType5.TEXT_SMALL, {
8671
8870
  prompt: tweetPrompt,
8672
8871
  max_tokens: 100,
8673
8872
  temperature: 0.8
@@ -8687,16 +8886,19 @@ Tweet:`;
8687
8886
  }
8688
8887
  const tweetUrl = `https://twitter.com/${client.profile.username}/status/${tweetId}`;
8689
8888
  logger9.info(`Successfully posted tweet: ${tweetId}`);
8690
- await runtime.createMemory({
8691
- entityId: runtime.agentId,
8692
- content: {
8693
- text: finalTweetText,
8694
- url: tweetUrl,
8695
- source: "twitter",
8696
- action: "POST_TWEET"
8889
+ await runtime.createMemory(
8890
+ {
8891
+ entityId: runtime.agentId,
8892
+ content: {
8893
+ text: finalTweetText,
8894
+ url: tweetUrl,
8895
+ source: "twitter",
8896
+ action: "POST_TWEET"
8897
+ },
8898
+ roomId: message.roomId
8697
8899
  },
8698
- roomId: message.roomId
8699
- }, "messages");
8900
+ "messages"
8901
+ );
8700
8902
  if (callback) {
8701
8903
  await callback({
8702
8904
  text: `I've posted a tweet: "${finalTweetText}"