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