@elizaos/plugin-twitter 1.2.11 → 1.2.12

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
@@ -1948,7 +1948,7 @@ import {
1948
1948
  } from "@elizaos/core";
1949
1949
 
1950
1950
  // src/constants.ts
1951
- var TWEET_MAX_LENGTH = 4e3;
1951
+ var TWEET_MAX_LENGTH = 280;
1952
1952
 
1953
1953
  // src/utils/error-handler.ts
1954
1954
  import { logger } from "@elizaos/core";
@@ -6894,20 +6894,21 @@ Response (YES/NO):`;
6894
6894
 
6895
6895
  // src/post.ts
6896
6896
  import {
6897
- ChannelType as ChannelType2,
6898
6897
  createUniqueUuid as createUniqueUuid3,
6899
6898
  logger as logger4,
6900
6899
  ModelType as ModelType2
6901
6900
  } from "@elizaos/core";
6902
6901
  var TwitterPostClient = class {
6902
+ // Add lock to prevent concurrent posting
6903
6903
  /**
6904
- * Constructor for initializing a new Twitter client with the provided client, runtime, and state
6905
- * @param {ClientBase} client - The client used for interacting with Twitter API
6906
- * @param {IAgentRuntime} runtime - The runtime environment for the agent
6904
+ * Creates an instance of TwitterPostClient.
6905
+ * @param {ClientBase} client - The client instance.
6906
+ * @param {IAgentRuntime} runtime - The runtime instance.
6907
6907
  * @param {any} state - The state object containing configuration settings
6908
6908
  */
6909
6909
  constructor(client, runtime, state) {
6910
6910
  this.isRunning = false;
6911
+ this.isPosting = false;
6911
6912
  this.client = client;
6912
6913
  this.state = state;
6913
6914
  this.runtime = runtime;
@@ -6964,6 +6965,11 @@ var TwitterPostClient = class {
6964
6965
  */
6965
6966
  async generateNewTweet() {
6966
6967
  logger4.info("Attempting to generate new tweet...");
6968
+ if (this.isPosting) {
6969
+ logger4.info("Already posting a tweet, skipping concurrent attempt");
6970
+ return;
6971
+ }
6972
+ this.isPosting = true;
6967
6973
  try {
6968
6974
  const userId = this.client.profile?.id;
6969
6975
  if (!userId) {
@@ -6985,24 +6991,39 @@ var TwitterPostClient = class {
6985
6991
  const tweetPrompt = `You are ${this.runtime.character.name}.
6986
6992
  ${this.runtime.character.bio}
6987
6993
 
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
+ CRITICAL: Generate a tweet that sounds like YOU, not a generic motivational poster or LinkedIn influencer.
6995
+
6996
+ ${this.runtime.character.messageExamples && this.runtime.character.messageExamples.length > 0 ? `
6997
+ Example tweets that capture your voice:
6998
+ ${this.runtime.character.messageExamples.map(
6999
+ (example) => Array.isArray(example) ? example[1]?.content?.text || "" : example
7000
+ ).filter(Boolean).slice(0, 5).join("\n")}
7001
+ ` : ""}
7002
+
7003
+ Style guidelines:
7004
+ - Be authentic, opinionated, and specific - no generic platitudes
7005
+ - Use your unique voice and perspective
7006
+ - Share hot takes, unpopular opinions, or specific insights
7007
+ - Be conversational, not preachy
7008
+ - If you use emojis, use them sparingly and purposefully
7009
+ - Length: 50-280 characters (keep it punchy)
7010
+ - NO hashtags unless absolutely essential
7011
+ - NO generic motivational content
7012
+
7013
+ Your interests: ${this.runtime.character.topics?.join(", ") || "technology, crypto, AI"}
6994
7014
 
6995
- Your topics of interest: ${this.runtime.character.topics?.join(", ") || "general topics"}
7015
+ ${this.runtime.character.style ? `Your style: ${typeof this.runtime.character.style === "object" ? this.runtime.character.style.all?.join(", ") || JSON.stringify(this.runtime.character.style) : this.runtime.character.style}` : ""}
6996
7016
 
6997
7017
  Recent context:
6998
- ${state.recentMemories?.slice(0, 5).map((m) => m.content.text).join("\n") || "No recent context"}
7018
+ ${state.recentMemories?.slice(0, 3).map((m) => m.content.text).join("\n") || "No recent context"}
6999
7019
 
7000
- Generate a single tweet:`;
7020
+ Generate a single tweet that sounds like YOU would actually write it:`;
7001
7021
  const generatedContent = await this.runtime.useModel(
7002
7022
  ModelType2.TEXT_SMALL,
7003
7023
  {
7004
7024
  prompt: tweetPrompt,
7005
- temperature: 0.8,
7025
+ temperature: 0.9,
7026
+ // Increased for more creativity
7006
7027
  maxTokens: 100
7007
7028
  }
7008
7029
  );
@@ -7015,6 +7036,33 @@ Generate a single tweet:`;
7015
7036
  logger4.error("Error in generated content:", tweetText);
7016
7037
  return;
7017
7038
  }
7039
+ if (tweetText.length > 280) {
7040
+ logger4.warn(`Generated tweet too long (${tweetText.length} chars), truncating...`);
7041
+ const sentences = tweetText.match(/[^.!?]+[.!?]+/g) || [tweetText];
7042
+ let truncated = "";
7043
+ for (const sentence of sentences) {
7044
+ if ((truncated + sentence).length <= 280) {
7045
+ truncated += sentence;
7046
+ } else {
7047
+ break;
7048
+ }
7049
+ }
7050
+ const finalTweet = truncated.trim() || tweetText.substring(0, 277) + "...";
7051
+ logger4.info(`Truncated tweet: ${finalTweet}`);
7052
+ if (this.isDryRun) {
7053
+ logger4.info(`[DRY RUN] Would post tweet: ${finalTweet}`);
7054
+ return;
7055
+ }
7056
+ const result2 = await this.postToTwitter(finalTweet, []);
7057
+ if (result2 === null) {
7058
+ logger4.info("Skipped posting duplicate tweet");
7059
+ return;
7060
+ }
7061
+ const tweetId2 = result2.id;
7062
+ logger4.info(`Tweet posted successfully! ID: ${tweetId2}`);
7063
+ logger4.info("Tweet posted successfully (memory saving disabled due to room constraints)");
7064
+ return;
7065
+ }
7018
7066
  logger4.info(`Generated tweet: ${tweetText}`);
7019
7067
  if (this.isDryRun) {
7020
7068
  logger4.info(`[DRY RUN] Would post tweet: ${tweetText}`);
@@ -7029,28 +7077,12 @@ Generate a single tweet:`;
7029
7077
  logger4.info(`Tweet posted successfully! ID: ${tweetId}`);
7030
7078
  if (result) {
7031
7079
  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");
7080
+ logger4.info("Tweet posted successfully (memory saving temporarily disabled)");
7051
7081
  }
7052
7082
  } catch (error) {
7053
7083
  logger4.error("Error generating tweet:", error);
7084
+ } finally {
7085
+ this.isPosting = false;
7054
7086
  }
7055
7087
  }
7056
7088
  /**
@@ -8842,34 +8874,70 @@ var postTweetAction = {
8842
8874
  if (!client.twitterClient) {
8843
8875
  await client.init();
8844
8876
  }
8877
+ let text = message.content?.text?.trim();
8878
+ if (!text) {
8879
+ logger9.error("No text content for tweet");
8880
+ if (callback) {
8881
+ callback({
8882
+ text: "I need something to tweet! Please provide the text.",
8883
+ action: "POST_TWEET"
8884
+ });
8885
+ }
8886
+ return;
8887
+ }
8888
+ if (text.length > 280) {
8889
+ logger9.info(`Truncating tweet from ${text.length} to 280 characters`);
8890
+ const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
8891
+ let truncated = "";
8892
+ for (const sentence of sentences) {
8893
+ if ((truncated + sentence).length <= 280) {
8894
+ truncated += sentence;
8895
+ } else {
8896
+ break;
8897
+ }
8898
+ }
8899
+ text = truncated.trim() || text.substring(0, 277) + "...";
8900
+ logger9.info(`Truncated tweet: ${text}`);
8901
+ }
8845
8902
  if (!client.profile) {
8846
8903
  throw new Error(
8847
8904
  "Twitter client not properly initialized - no profile found"
8848
8905
  );
8849
8906
  }
8850
- const tweetText = message.content?.text?.trim() || "";
8851
- let finalTweetText = tweetText;
8852
- if (tweetText.length < 50 || tweetText.toLowerCase().includes("post") || tweetText.toLowerCase().includes("tweet")) {
8853
- const tweetPrompt = `You are ${runtime.character.name}. Create an interesting tweet based on this context:
8907
+ let finalTweetText = text;
8908
+ if (text.length < 50 || text.toLowerCase().includes("post") || text.toLowerCase().includes("tweet")) {
8909
+ const tweetPrompt = `You are ${runtime.character.name}.
8910
+ ${runtime.character.bio || ""}
8854
8911
 
8855
- Context: ${tweetText}
8912
+ CRITICAL: Generate a tweet based on the context that sounds like YOU, not generic corporate speak.
8856
8913
 
8857
- Your interests: ${runtime.character.topics?.join(", ") || "technology, AI, web3"}
8858
- Your style: ${runtime.character.style?.all?.join(", ") || "thoughtful, engaging"}
8914
+ Context: ${text}
8859
8915
 
8860
- Generate a tweet that:
8861
- - Is under 280 characters
8862
- - Reflects your personality and interests
8863
- - Is engaging and conversational
8864
- - Doesn't use hashtags unless truly relevant
8865
- - Doesn't ask questions at the end
8866
- - Is not generic or promotional
8916
+ ${runtime.character.messageExamples && runtime.character.messageExamples.length > 0 ? `
8917
+ Your voice examples:
8918
+ ${runtime.character.messageExamples.map(
8919
+ (example) => Array.isArray(example) ? example[1]?.content?.text || "" : example
8920
+ ).filter(Boolean).slice(0, 3).join("\n")}
8921
+ ` : ""}
8922
+
8923
+ Style rules:
8924
+ - Be specific, opinionated, authentic
8925
+ - No generic motivational content or platitudes
8926
+ - Share actual insights, hot takes, or unique perspectives
8927
+ - Keep it conversational and punchy
8928
+ - Under 280 characters
8929
+ - Skip hashtags unless essential
8930
+ - Don't end with generic questions
8931
+
8932
+ Your interests: ${runtime.character.topics?.join(", ") || "technology, AI, web3"}
8933
+ ${runtime.character.style ? `Your style: ${typeof runtime.character.style === "object" ? runtime.character.style.all?.join(", ") || "" : runtime.character.style}` : ""}
8867
8934
 
8868
8935
  Tweet:`;
8869
8936
  const response = await runtime.useModel(ModelType5.TEXT_SMALL, {
8870
8937
  prompt: tweetPrompt,
8871
8938
  max_tokens: 100,
8872
- temperature: 0.8
8939
+ temperature: 0.9
8940
+ // Higher for more creativity
8873
8941
  });
8874
8942
  finalTweetText = response.trim();
8875
8943
  }
@@ -8910,7 +8978,7 @@ View it here: ${tweetUrl}`,
8910
8978
  }
8911
8979
  });
8912
8980
  }
8913
- return true;
8981
+ return;
8914
8982
  } else {
8915
8983
  throw new Error("Failed to post tweet - no response data");
8916
8984
  }
@@ -8922,7 +8990,7 @@ View it here: ${tweetUrl}`,
8922
8990
  metadata: { error: error.message }
8923
8991
  });
8924
8992
  }
8925
- return false;
8993
+ return;
8926
8994
  }
8927
8995
  },
8928
8996
  examples: [