@elizaos/plugin-twitter 1.2.10 → 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 +382 -195
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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(
|
|
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(
|
|
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(
|
|
826
|
-
|
|
827
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
6246
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
6480
|
-
|
|
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 =
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
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(
|
|
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
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
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
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
roomId,
|
|
6955
|
-
source: "twitter"
|
|
7004
|
+
prompt: tweetPrompt,
|
|
7005
|
+
temperature: 0.8,
|
|
7006
|
+
maxTokens: 100
|
|
6956
7007
|
}
|
|
6957
7008
|
);
|
|
6958
|
-
|
|
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
|
|
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
|
-
-
|
|
7030
|
-
- Direct mentions
|
|
7031
|
-
-
|
|
7032
|
-
-
|
|
7033
|
-
-
|
|
7034
|
-
-
|
|
7035
|
-
-
|
|
7036
|
-
|
|
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] -
|
|
7040
|
-
[RETWEET] -
|
|
7041
|
-
[QUOTE] -
|
|
7042
|
-
[REPLY] -
|
|
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(
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,7 +7545,7 @@ ${tweet.text}`;
|
|
|
7438
7545
|
import {
|
|
7439
7546
|
createUniqueUuid as createUniqueUuid5,
|
|
7440
7547
|
logger as logger6,
|
|
7441
|
-
ModelType as
|
|
7548
|
+
ModelType as ModelType4
|
|
7442
7549
|
} from "@elizaos/core";
|
|
7443
7550
|
var TwitterDiscoveryClient = class {
|
|
7444
7551
|
constructor(client, runtime, state) {
|
|
@@ -7489,7 +7596,9 @@ var TwitterDiscoveryClient = class {
|
|
|
7489
7596
|
topics = this.extractTopicsFromBio(character.bio);
|
|
7490
7597
|
}
|
|
7491
7598
|
} else {
|
|
7492
|
-
logger6.warn(
|
|
7599
|
+
logger6.warn(
|
|
7600
|
+
"Character not available in runtime, using default topics for discovery"
|
|
7601
|
+
);
|
|
7493
7602
|
}
|
|
7494
7603
|
return {
|
|
7495
7604
|
topics,
|
|
@@ -7500,11 +7609,17 @@ var TwitterDiscoveryClient = class {
|
|
|
7500
7609
|
getSetting(this.runtime, "TWITTER_MAX_FOLLOWS_PER_CYCLE") || process.env.TWITTER_MAX_FOLLOWS_PER_CYCLE || "5"
|
|
7501
7610
|
),
|
|
7502
7611
|
maxEngagementsPerCycle: parseInt(
|
|
7503
|
-
getSetting(
|
|
7612
|
+
getSetting(
|
|
7613
|
+
this.runtime,
|
|
7614
|
+
"TWITTER_MAX_ENGAGEMENTS_PER_RUN"
|
|
7615
|
+
) || process.env.TWITTER_MAX_ENGAGEMENTS_PER_RUN || "10"
|
|
7504
7616
|
),
|
|
7505
|
-
likeThreshold: 0.
|
|
7506
|
-
|
|
7507
|
-
|
|
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
|
|
7508
7623
|
};
|
|
7509
7624
|
}
|
|
7510
7625
|
extractTopicsFromBio(bio) {
|
|
@@ -7512,7 +7627,16 @@ var TwitterDiscoveryClient = class {
|
|
|
7512
7627
|
return [];
|
|
7513
7628
|
}
|
|
7514
7629
|
const bioText = Array.isArray(bio) ? bio.join(" ") : bio;
|
|
7515
|
-
const words = bioText.toLowerCase().split(/\s+/).filter((word) => word.length > 4).filter(
|
|
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
|
+
);
|
|
7516
7640
|
return [...new Set(words)].slice(0, 5);
|
|
7517
7641
|
}
|
|
7518
7642
|
async start() {
|
|
@@ -7533,7 +7657,9 @@ var TwitterDiscoveryClient = class {
|
|
|
7533
7657
|
);
|
|
7534
7658
|
const variance = Math.random() * 20 - 10;
|
|
7535
7659
|
const nextInterval = (baseInterval + variance) * 60 * 1e3;
|
|
7536
|
-
logger6.info(
|
|
7660
|
+
logger6.info(
|
|
7661
|
+
`Next discovery cycle in ${(baseInterval + variance).toFixed(1)} minutes`
|
|
7662
|
+
);
|
|
7537
7663
|
if (this.isRunning) {
|
|
7538
7664
|
setTimeout(discoveryLoop, nextInterval);
|
|
7539
7665
|
}
|
|
@@ -7548,7 +7674,9 @@ var TwitterDiscoveryClient = class {
|
|
|
7548
7674
|
logger6.info("Starting Twitter discovery cycle...");
|
|
7549
7675
|
const discoveries = await this.discoverContent();
|
|
7550
7676
|
const { tweets, accounts } = discoveries;
|
|
7551
|
-
logger6.info(
|
|
7677
|
+
logger6.info(
|
|
7678
|
+
`Discovered ${tweets.length} tweets and ${accounts.length} accounts`
|
|
7679
|
+
);
|
|
7552
7680
|
const followedCount = await this.processAccounts(accounts);
|
|
7553
7681
|
const engagementCount = await this.processTweets(tweets);
|
|
7554
7682
|
logger6.info(
|
|
@@ -7561,9 +7689,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7561
7689
|
try {
|
|
7562
7690
|
const topicContent = await this.discoverFromTopics();
|
|
7563
7691
|
allTweets.push(...topicContent.tweets);
|
|
7564
|
-
topicContent.accounts.forEach(
|
|
7565
|
-
(acc) => allAccounts.set(acc.user.id, acc)
|
|
7566
|
-
);
|
|
7692
|
+
topicContent.accounts.forEach((acc) => allAccounts.set(acc.user.id, acc));
|
|
7567
7693
|
} catch (error) {
|
|
7568
7694
|
logger6.error("Failed to discover from topics:", error);
|
|
7569
7695
|
}
|
|
@@ -7586,7 +7712,9 @@ var TwitterDiscoveryClient = class {
|
|
|
7586
7712
|
logger6.error("Failed to discover from popular accounts:", error);
|
|
7587
7713
|
}
|
|
7588
7714
|
const sortedTweets = allTweets.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, 50);
|
|
7589
|
-
const sortedAccounts = Array.from(allAccounts.values()).sort(
|
|
7715
|
+
const sortedAccounts = Array.from(allAccounts.values()).sort(
|
|
7716
|
+
(a, b) => b.qualityScore * b.relevanceScore - a.qualityScore * a.relevanceScore
|
|
7717
|
+
).slice(0, 20);
|
|
7590
7718
|
return { tweets: sortedTweets, accounts: sortedAccounts };
|
|
7591
7719
|
}
|
|
7592
7720
|
async discoverFromTopics() {
|
|
@@ -7652,17 +7780,17 @@ var TwitterDiscoveryClient = class {
|
|
|
7652
7780
|
);
|
|
7653
7781
|
for (const tweet of searchResults.tweets) {
|
|
7654
7782
|
const engagementScore = (tweet.likes || 0) + (tweet.retweets || 0) * 2;
|
|
7655
|
-
if (engagementScore <
|
|
7783
|
+
if (engagementScore < 10) continue;
|
|
7656
7784
|
const scored = this.scoreTweet(tweet, "thread");
|
|
7657
7785
|
tweets.push(scored);
|
|
7658
7786
|
const account = this.scoreAccount({
|
|
7659
7787
|
id: tweet.userId,
|
|
7660
7788
|
username: tweet.username,
|
|
7661
7789
|
name: tweet.name || tweet.username,
|
|
7662
|
-
followersCount:
|
|
7663
|
-
//
|
|
7790
|
+
followersCount: 1e3
|
|
7791
|
+
// Reasonable estimate for engaged users
|
|
7664
7792
|
});
|
|
7665
|
-
if (account.qualityScore > 0.
|
|
7793
|
+
if (account.qualityScore > 0.5) {
|
|
7666
7794
|
accounts.set(tweet.userId, account);
|
|
7667
7795
|
}
|
|
7668
7796
|
}
|
|
@@ -7687,7 +7815,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7687
7815
|
);
|
|
7688
7816
|
for (const tweet of results.tweets) {
|
|
7689
7817
|
const engagement = (tweet.likes || 0) + (tweet.retweets || 0) * 2;
|
|
7690
|
-
if (engagement <
|
|
7818
|
+
if (engagement < 5) continue;
|
|
7691
7819
|
const scored = this.scoreTweet(tweet, "topic");
|
|
7692
7820
|
tweets.push(scored);
|
|
7693
7821
|
const estimatedFollowers = Math.max(
|
|
@@ -7706,7 +7834,10 @@ var TwitterDiscoveryClient = class {
|
|
|
7706
7834
|
}
|
|
7707
7835
|
}
|
|
7708
7836
|
} catch (error) {
|
|
7709
|
-
logger6.error(
|
|
7837
|
+
logger6.error(
|
|
7838
|
+
`Failed to discover popular accounts for ${topic}:`,
|
|
7839
|
+
error
|
|
7840
|
+
);
|
|
7710
7841
|
}
|
|
7711
7842
|
}
|
|
7712
7843
|
return { tweets, accounts: Array.from(accounts.values()) };
|
|
@@ -7716,12 +7847,15 @@ var TwitterDiscoveryClient = class {
|
|
|
7716
7847
|
scoreTweet(tweet, source) {
|
|
7717
7848
|
let relevanceScore = 0;
|
|
7718
7849
|
const sourceScores = {
|
|
7719
|
-
topic: 0.
|
|
7720
|
-
thread: 0.
|
|
7850
|
+
topic: 0.4,
|
|
7851
|
+
thread: 0.35
|
|
7721
7852
|
};
|
|
7722
7853
|
relevanceScore += sourceScores[source];
|
|
7723
7854
|
const engagementScore = Math.min(
|
|
7724
|
-
(tweet.likes || 0) /
|
|
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)
|
|
7725
7859
|
0.3
|
|
7726
7860
|
);
|
|
7727
7861
|
relevanceScore += engagementScore;
|
|
@@ -7729,7 +7863,7 @@ var TwitterDiscoveryClient = class {
|
|
|
7729
7863
|
const topicMatches = this.config.topics.filter(
|
|
7730
7864
|
(topic) => textLower.includes(topic.toLowerCase())
|
|
7731
7865
|
).length;
|
|
7732
|
-
relevanceScore += Math.min(topicMatches * 0.
|
|
7866
|
+
relevanceScore += Math.min(topicMatches * 0.15, 0.3);
|
|
7733
7867
|
relevanceScore = Math.min(relevanceScore, 1);
|
|
7734
7868
|
let engagementType = "skip";
|
|
7735
7869
|
if (relevanceScore >= this.config.quoteThreshold) {
|
|
@@ -7783,7 +7917,10 @@ var TwitterDiscoveryClient = class {
|
|
|
7783
7917
|
followedCount++;
|
|
7784
7918
|
await this.delay(2e3 + Math.random() * 3e3);
|
|
7785
7919
|
} catch (error) {
|
|
7786
|
-
logger6.error(
|
|
7920
|
+
logger6.error(
|
|
7921
|
+
`Failed to follow @${scoredAccount.user.username}:`,
|
|
7922
|
+
error
|
|
7923
|
+
);
|
|
7787
7924
|
}
|
|
7788
7925
|
}
|
|
7789
7926
|
return followedCount;
|
|
@@ -7794,51 +7931,76 @@ var TwitterDiscoveryClient = class {
|
|
|
7794
7931
|
if (engagementCount >= this.config.maxEngagementsPerCycle) break;
|
|
7795
7932
|
if (scoredTweet.engagementType === "skip") continue;
|
|
7796
7933
|
try {
|
|
7797
|
-
const tweetMemoryId = createUniqueUuid5(
|
|
7934
|
+
const tweetMemoryId = createUniqueUuid5(
|
|
7935
|
+
this.runtime,
|
|
7936
|
+
scoredTweet.tweet.id
|
|
7937
|
+
);
|
|
7798
7938
|
const existingMemory = await this.runtime.getMemoryById(tweetMemoryId);
|
|
7799
7939
|
if (existingMemory) {
|
|
7800
|
-
logger6.debug(
|
|
7940
|
+
logger6.debug(
|
|
7941
|
+
`Already engaged with tweet ${scoredTweet.tweet.id}, skipping`
|
|
7942
|
+
);
|
|
7801
7943
|
continue;
|
|
7802
7944
|
}
|
|
7803
7945
|
switch (scoredTweet.engagementType) {
|
|
7804
7946
|
case "like":
|
|
7805
7947
|
if (this.isDryRun) {
|
|
7806
|
-
logger6.info(
|
|
7948
|
+
logger6.info(
|
|
7949
|
+
`[DRY RUN] Would like tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
|
|
7950
|
+
);
|
|
7807
7951
|
} else {
|
|
7808
7952
|
await this.twitterClient.likeTweet(scoredTweet.tweet.id);
|
|
7809
|
-
logger6.info(
|
|
7953
|
+
logger6.info(
|
|
7954
|
+
`Liked tweet: ${scoredTweet.tweet.id} (score: ${scoredTweet.relevanceScore.toFixed(2)})`
|
|
7955
|
+
);
|
|
7810
7956
|
}
|
|
7811
7957
|
break;
|
|
7812
7958
|
case "reply":
|
|
7813
7959
|
const replyText = await this.generateReply(scoredTweet.tweet);
|
|
7814
7960
|
if (this.isDryRun) {
|
|
7815
|
-
logger6.info(
|
|
7961
|
+
logger6.info(
|
|
7962
|
+
`[DRY RUN] Would reply to tweet ${scoredTweet.tweet.id} with: "${replyText}"`
|
|
7963
|
+
);
|
|
7816
7964
|
} else {
|
|
7817
|
-
await this.twitterClient.sendTweet(
|
|
7965
|
+
await this.twitterClient.sendTweet(
|
|
7966
|
+
replyText,
|
|
7967
|
+
scoredTweet.tweet.id
|
|
7968
|
+
);
|
|
7818
7969
|
logger6.info(`Replied to tweet: ${scoredTweet.tweet.id}`);
|
|
7819
7970
|
}
|
|
7820
7971
|
break;
|
|
7821
7972
|
case "quote":
|
|
7822
7973
|
const quoteText = await this.generateQuote(scoredTweet.tweet);
|
|
7823
7974
|
if (this.isDryRun) {
|
|
7824
|
-
logger6.info(
|
|
7975
|
+
logger6.info(
|
|
7976
|
+
`[DRY RUN] Would quote tweet ${scoredTweet.tweet.id} with: "${quoteText}"`
|
|
7977
|
+
);
|
|
7825
7978
|
} else {
|
|
7826
|
-
await this.twitterClient.sendQuoteTweet(
|
|
7979
|
+
await this.twitterClient.sendQuoteTweet(
|
|
7980
|
+
quoteText,
|
|
7981
|
+
scoredTweet.tweet.id
|
|
7982
|
+
);
|
|
7827
7983
|
logger6.info(`Quoted tweet: ${scoredTweet.tweet.id}`);
|
|
7828
7984
|
}
|
|
7829
7985
|
break;
|
|
7830
7986
|
}
|
|
7831
|
-
await this.saveEngagementMemory(
|
|
7987
|
+
await this.saveEngagementMemory(
|
|
7988
|
+
scoredTweet.tweet,
|
|
7989
|
+
scoredTweet.engagementType
|
|
7990
|
+
);
|
|
7832
7991
|
engagementCount++;
|
|
7833
7992
|
await this.delay(3e3 + Math.random() * 5e3);
|
|
7834
7993
|
} catch (error) {
|
|
7835
|
-
logger6.error(
|
|
7994
|
+
logger6.error(
|
|
7995
|
+
`Failed to engage with tweet ${scoredTweet.tweet.id}:`,
|
|
7996
|
+
error
|
|
7997
|
+
);
|
|
7836
7998
|
}
|
|
7837
7999
|
}
|
|
7838
8000
|
return engagementCount;
|
|
7839
8001
|
}
|
|
7840
8002
|
async checkIfFollowing(userId) {
|
|
7841
|
-
const embedding = await this.runtime.useModel(
|
|
8003
|
+
const embedding = await this.runtime.useModel(ModelType4.TEXT_EMBEDDING, {
|
|
7842
8004
|
text: `followed twitter user ${userId}`
|
|
7843
8005
|
});
|
|
7844
8006
|
const followMemories = await this.runtime.searchMemories({
|
|
@@ -7874,7 +8036,7 @@ Keep the reply:
|
|
|
7874
8036
|
- Respectful and constructive
|
|
7875
8037
|
|
|
7876
8038
|
Reply:`;
|
|
7877
|
-
const response = await this.runtime.useModel(
|
|
8039
|
+
const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
|
|
7878
8040
|
prompt,
|
|
7879
8041
|
max_tokens: 100,
|
|
7880
8042
|
temperature: 0.8
|
|
@@ -7906,7 +8068,7 @@ Create a quote tweet that:
|
|
|
7906
8068
|
- Encourages further discussion
|
|
7907
8069
|
|
|
7908
8070
|
Quote tweet:`;
|
|
7909
|
-
const response = await this.runtime.useModel(
|
|
8071
|
+
const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
|
|
7910
8072
|
prompt,
|
|
7911
8073
|
max_tokens: 100,
|
|
7912
8074
|
temperature: 0.8
|
|
@@ -7914,37 +8076,43 @@ Quote tweet:`;
|
|
|
7914
8076
|
return response.trim();
|
|
7915
8077
|
}
|
|
7916
8078
|
async saveEngagementMemory(tweet, engagementType) {
|
|
7917
|
-
const memoryId = await this.runtime.createMemory(
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
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)
|
|
7928
8093
|
},
|
|
7929
|
-
|
|
7930
|
-
|
|
8094
|
+
"messages"
|
|
8095
|
+
);
|
|
7931
8096
|
}
|
|
7932
8097
|
async saveFollowMemory(user) {
|
|
7933
|
-
const memoryId = await this.runtime.createMemory(
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
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`)
|
|
7945
8113
|
},
|
|
7946
|
-
|
|
7947
|
-
|
|
8114
|
+
"messages"
|
|
8115
|
+
);
|
|
7948
8116
|
}
|
|
7949
8117
|
delay(ms) {
|
|
7950
8118
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -8008,7 +8176,9 @@ var RequestQueue = class {
|
|
|
8008
8176
|
await this.exponentialBackoff(retryCount);
|
|
8009
8177
|
break;
|
|
8010
8178
|
} else {
|
|
8011
|
-
logger7.error(
|
|
8179
|
+
logger7.error(
|
|
8180
|
+
`Max retries (${this.maxRetries}) exceeded for request, skipping`
|
|
8181
|
+
);
|
|
8012
8182
|
this.retryAttempts.delete(request);
|
|
8013
8183
|
}
|
|
8014
8184
|
}
|
|
@@ -8541,13 +8711,17 @@ var TwitterClientInstance = class {
|
|
|
8541
8711
|
constructor(runtime, state) {
|
|
8542
8712
|
this.client = new ClientBase(runtime, state);
|
|
8543
8713
|
const postEnabledSetting = getSetting(runtime, "TWITTER_ENABLE_POST") ?? process.env.TWITTER_ENABLE_POST;
|
|
8544
|
-
logger8.debug(
|
|
8714
|
+
logger8.debug(
|
|
8715
|
+
`TWITTER_ENABLE_POST setting value: ${JSON.stringify(postEnabledSetting)}, type: ${typeof postEnabledSetting}`
|
|
8716
|
+
);
|
|
8545
8717
|
const postEnabled = postEnabledSetting === "true" || postEnabledSetting === true;
|
|
8546
8718
|
if (postEnabled) {
|
|
8547
8719
|
logger8.info("Twitter posting is ENABLED - creating post client");
|
|
8548
8720
|
this.post = new TwitterPostClient(this.client, runtime, state);
|
|
8549
8721
|
} else {
|
|
8550
|
-
logger8.info(
|
|
8722
|
+
logger8.info(
|
|
8723
|
+
"Twitter posting is DISABLED - set TWITTER_ENABLE_POST=true to enable automatic posting"
|
|
8724
|
+
);
|
|
8551
8725
|
}
|
|
8552
8726
|
const repliesEnabled = (getSetting(runtime, "TWITTER_ENABLE_REPLIES") ?? process.env.TWITTER_ENABLE_REPLIES) !== "false";
|
|
8553
8727
|
if (repliesEnabled) {
|
|
@@ -8572,7 +8746,9 @@ var TwitterClientInstance = class {
|
|
|
8572
8746
|
logger8.info("Twitter discovery service is ENABLED");
|
|
8573
8747
|
this.discovery = new TwitterDiscoveryClient(this.client, runtime, state);
|
|
8574
8748
|
} else {
|
|
8575
|
-
logger8.info(
|
|
8749
|
+
logger8.info(
|
|
8750
|
+
"Twitter discovery service is DISABLED - set TWITTER_ENABLE_DISCOVERY=true to enable"
|
|
8751
|
+
);
|
|
8576
8752
|
}
|
|
8577
8753
|
}
|
|
8578
8754
|
};
|
|
@@ -8635,11 +8811,17 @@ var TwitterService = _TwitterService;
|
|
|
8635
8811
|
// src/actions/postTweet.ts
|
|
8636
8812
|
import {
|
|
8637
8813
|
logger as logger9,
|
|
8638
|
-
ModelType as
|
|
8814
|
+
ModelType as ModelType5
|
|
8639
8815
|
} from "@elizaos/core";
|
|
8640
8816
|
var postTweetAction = {
|
|
8641
8817
|
name: "POST_TWEET",
|
|
8642
|
-
similes: [
|
|
8818
|
+
similes: [
|
|
8819
|
+
"TWEET",
|
|
8820
|
+
"SEND_TWEET",
|
|
8821
|
+
"TWITTER_POST",
|
|
8822
|
+
"POST_ON_TWITTER",
|
|
8823
|
+
"SHARE_ON_TWITTER"
|
|
8824
|
+
],
|
|
8643
8825
|
validate: async (runtime, message) => {
|
|
8644
8826
|
logger9.debug("Validating POST_TWEET action");
|
|
8645
8827
|
const text = message.content?.text?.trim();
|
|
@@ -8661,7 +8843,9 @@ var postTweetAction = {
|
|
|
8661
8843
|
await client.init();
|
|
8662
8844
|
}
|
|
8663
8845
|
if (!client.profile) {
|
|
8664
|
-
throw new Error(
|
|
8846
|
+
throw new Error(
|
|
8847
|
+
"Twitter client not properly initialized - no profile found"
|
|
8848
|
+
);
|
|
8665
8849
|
}
|
|
8666
8850
|
const tweetText = message.content?.text?.trim() || "";
|
|
8667
8851
|
let finalTweetText = tweetText;
|
|
@@ -8682,7 +8866,7 @@ Generate a tweet that:
|
|
|
8682
8866
|
- Is not generic or promotional
|
|
8683
8867
|
|
|
8684
8868
|
Tweet:`;
|
|
8685
|
-
const response = await runtime.useModel(
|
|
8869
|
+
const response = await runtime.useModel(ModelType5.TEXT_SMALL, {
|
|
8686
8870
|
prompt: tweetPrompt,
|
|
8687
8871
|
max_tokens: 100,
|
|
8688
8872
|
temperature: 0.8
|
|
@@ -8702,16 +8886,19 @@ Tweet:`;
|
|
|
8702
8886
|
}
|
|
8703
8887
|
const tweetUrl = `https://twitter.com/${client.profile.username}/status/${tweetId}`;
|
|
8704
8888
|
logger9.info(`Successfully posted tweet: ${tweetId}`);
|
|
8705
|
-
await runtime.createMemory(
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
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
|
|
8712
8899
|
},
|
|
8713
|
-
|
|
8714
|
-
|
|
8900
|
+
"messages"
|
|
8901
|
+
);
|
|
8715
8902
|
if (callback) {
|
|
8716
8903
|
await callback({
|
|
8717
8904
|
text: `I've posted a tweet: "${finalTweetText}"
|