@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 +119 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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 =
|
|
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
|
-
*
|
|
6905
|
-
* @param {ClientBase} client - The client
|
|
6906
|
-
* @param {IAgentRuntime} runtime - The runtime
|
|
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
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
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
|
-
|
|
8912
|
+
CRITICAL: Generate a tweet based on the context that sounds like YOU, not generic corporate speak.
|
|
8856
8913
|
|
|
8857
|
-
|
|
8858
|
-
Your style: ${runtime.character.style?.all?.join(", ") || "thoughtful, engaging"}
|
|
8914
|
+
Context: ${text}
|
|
8859
8915
|
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
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.
|
|
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
|
|
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
|
|
8993
|
+
return;
|
|
8926
8994
|
}
|
|
8927
8995
|
},
|
|
8928
8996
|
examples: [
|