@elizaos/plugin-twitter 1.0.0-alpha.7 → 1.0.0-beta.1
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 +1196 -1676
- package/dist/index.js.map +1 -1
- package/dist/{node-GVRE5HAO.js → node-IZDTQWG6.js} +1 -1
- package/dist/node-IZDTQWG6.js.map +1 -0
- package/package.json +56 -53
- package/dist/node-GVRE5HAO.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
+
ChannelType as ChannelType7,
|
|
4
|
+
EventType as EventType4,
|
|
5
|
+
Role,
|
|
3
6
|
Service,
|
|
4
|
-
|
|
7
|
+
createUniqueUuid as createUniqueUuid8,
|
|
8
|
+
logger as logger10
|
|
5
9
|
} from "@elizaos/core";
|
|
6
10
|
|
|
7
11
|
// src/actions/spaceJoin.ts
|
|
8
12
|
import {
|
|
9
|
-
logger as
|
|
13
|
+
logger as logger5,
|
|
10
14
|
stringToUuid
|
|
11
15
|
} from "@elizaos/core";
|
|
12
16
|
|
|
13
17
|
// src/spaces.ts
|
|
14
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
ChannelType as ChannelType3,
|
|
20
|
+
ModelType as ModelType3,
|
|
21
|
+
createUniqueUuid as createUniqueUuid3,
|
|
22
|
+
logger as logger4
|
|
23
|
+
} from "@elizaos/core";
|
|
15
24
|
|
|
16
25
|
// src/client/api.ts
|
|
17
26
|
import { Headers as Headers2 } from "headers-polyfill";
|
|
@@ -73,7 +82,7 @@ var Platform = class _Platform {
|
|
|
73
82
|
*/
|
|
74
83
|
static async importPlatform() {
|
|
75
84
|
if (PLATFORM_NODE) {
|
|
76
|
-
const { platform } = await import("./node-
|
|
85
|
+
const { platform } = await import("./node-IZDTQWG6.js");
|
|
77
86
|
return platform;
|
|
78
87
|
}
|
|
79
88
|
return genericPlatform;
|
|
@@ -263,10 +272,7 @@ import { CookieJar } from "tough-cookie";
|
|
|
263
272
|
import { TwitterApi } from "twitter-api-v2";
|
|
264
273
|
function withTransform(fetchFn, transform) {
|
|
265
274
|
return async (input, init) => {
|
|
266
|
-
const fetchArgs = await transform?.request?.(input, init) ?? [
|
|
267
|
-
input,
|
|
268
|
-
init
|
|
269
|
-
];
|
|
275
|
+
const fetchArgs = await transform?.request?.(input, init) ?? [input, init];
|
|
270
276
|
const res = await fetchFn(...fetchArgs);
|
|
271
277
|
return await transform?.response?.(res) ?? res;
|
|
272
278
|
};
|
|
@@ -279,12 +285,6 @@ var TwitterGuestAuth = class {
|
|
|
279
285
|
this.jar = new CookieJar();
|
|
280
286
|
this.v2Client = null;
|
|
281
287
|
}
|
|
282
|
-
bearerToken;
|
|
283
|
-
jar;
|
|
284
|
-
guestToken;
|
|
285
|
-
guestCreatedAt;
|
|
286
|
-
v2Client;
|
|
287
|
-
fetch;
|
|
288
288
|
cookieJar() {
|
|
289
289
|
return this.jar;
|
|
290
290
|
}
|
|
@@ -472,10 +472,7 @@ async function getProfile(username, auth) {
|
|
|
472
472
|
responsive_web_graphql_timeline_navigation_enabled: true
|
|
473
473
|
}) ?? ""
|
|
474
474
|
);
|
|
475
|
-
params.set(
|
|
476
|
-
"fieldToggles",
|
|
477
|
-
stringify({ withAuxiliaryUserLabels: false }) ?? ""
|
|
478
|
-
);
|
|
475
|
+
params.set("fieldToggles", stringify({ withAuxiliaryUserLabels: false }) ?? "");
|
|
479
476
|
const res = await requestApi(
|
|
480
477
|
`https://twitter.com/i/api/graphql/G3KGOASz96M-Qu0nwmGXNg/UserByScreenName?${params.toString()}`,
|
|
481
478
|
auth
|
|
@@ -568,9 +565,7 @@ async function getScreenNameByUserId(userId, auth) {
|
|
|
568
565
|
if (legacy.screen_name == null || legacy.screen_name.length === 0) {
|
|
569
566
|
return {
|
|
570
567
|
success: false,
|
|
571
|
-
err: new Error(
|
|
572
|
-
`Either user with ID ${userId} does not exist or is private.`
|
|
573
|
-
)
|
|
568
|
+
err: new Error(`Either user with ID ${userId} does not exist or is private.`)
|
|
574
569
|
};
|
|
575
570
|
}
|
|
576
571
|
return {
|
|
@@ -578,7 +573,7 @@ async function getScreenNameByUserId(userId, auth) {
|
|
|
578
573
|
value: legacy.screen_name
|
|
579
574
|
};
|
|
580
575
|
}
|
|
581
|
-
async function
|
|
576
|
+
async function getEntityIdByScreenName(screenName, auth) {
|
|
582
577
|
const cached = idCache.get(screenName);
|
|
583
578
|
if (cached != null) {
|
|
584
579
|
return { success: true, value: cached };
|
|
@@ -607,7 +602,6 @@ var TwitterUserAuthSubtask = Type.Object({
|
|
|
607
602
|
enter_text: Type.Optional(Type.Object({}))
|
|
608
603
|
});
|
|
609
604
|
var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
610
|
-
userProfile;
|
|
611
605
|
async isLoggedIn() {
|
|
612
606
|
const res = await requestApi(
|
|
613
607
|
"https://api.twitter.com/1.1/account/verify_credentials.json",
|
|
@@ -639,10 +633,7 @@ var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
|
639
633
|
} else if (next.subtask.subtask_id === "LoginEnterUserIdentifierSSO") {
|
|
640
634
|
next = await this.handleEnterUserIdentifierSSO(next, username);
|
|
641
635
|
} else if (next.subtask.subtask_id === "LoginEnterAlternateIdentifierSubtask") {
|
|
642
|
-
next = await this.handleEnterAlternateIdentifierSubtask(
|
|
643
|
-
next,
|
|
644
|
-
email
|
|
645
|
-
);
|
|
636
|
+
next = await this.handleEnterAlternateIdentifierSubtask(next, email);
|
|
646
637
|
} else if (next.subtask.subtask_id === "LoginEnterPassword") {
|
|
647
638
|
next = await this.handleEnterPassword(next, password);
|
|
648
639
|
} else if (next.subtask.subtask_id === "AccountDuplicationCheck") {
|
|
@@ -651,9 +642,7 @@ var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
|
651
642
|
if (twoFactorSecret) {
|
|
652
643
|
next = await this.handleTwoFactorAuthChallenge(next, twoFactorSecret);
|
|
653
644
|
} else {
|
|
654
|
-
throw new Error(
|
|
655
|
-
"Requested two factor authentication code but no secret provided"
|
|
656
|
-
);
|
|
645
|
+
throw new Error("Requested two factor authentication code but no secret provided");
|
|
657
646
|
}
|
|
658
647
|
} else if (next.subtask.subtask_id === "LoginAcid") {
|
|
659
648
|
next = await this.handleAcid(next, email);
|
|
@@ -674,11 +663,7 @@ var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
|
674
663
|
if (!this.isLoggedIn()) {
|
|
675
664
|
return;
|
|
676
665
|
}
|
|
677
|
-
await requestApi(
|
|
678
|
-
"https://api.twitter.com/1.1/account/logout.json",
|
|
679
|
-
this,
|
|
680
|
-
"POST"
|
|
681
|
-
);
|
|
666
|
+
await requestApi("https://api.twitter.com/1.1/account/logout.json", this, "POST");
|
|
682
667
|
this.deleteToken();
|
|
683
668
|
this.jar = new CookieJar2();
|
|
684
669
|
}
|
|
@@ -873,9 +858,7 @@ var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
|
873
858
|
if (flow.errors?.length) {
|
|
874
859
|
return {
|
|
875
860
|
status: "error",
|
|
876
|
-
err: new Error(
|
|
877
|
-
`Authentication error (${flow.errors[0].code}): ${flow.errors[0].message}`
|
|
878
|
-
)
|
|
861
|
+
err: new Error(`Authentication error (${flow.errors[0].code}): ${flow.errors[0].message}`)
|
|
879
862
|
};
|
|
880
863
|
}
|
|
881
864
|
if (typeof flow.flow_token !== "string") {
|
|
@@ -963,10 +946,7 @@ async function grokChat(options, auth) {
|
|
|
963
946
|
return {
|
|
964
947
|
conversationId,
|
|
965
948
|
message: firstChunk.result.message,
|
|
966
|
-
messages: [
|
|
967
|
-
...messages,
|
|
968
|
-
{ role: "assistant", content: firstChunk.result.message }
|
|
969
|
-
],
|
|
949
|
+
messages: [...messages, { role: "assistant", content: firstChunk.result.message }],
|
|
970
950
|
rateLimit: {
|
|
971
951
|
isRateLimited: true,
|
|
972
952
|
message: firstChunk.result.message,
|
|
@@ -996,19 +976,17 @@ function parseDirectMessageConversations(data, userId) {
|
|
|
996
976
|
const conversations = inboxState?.conversations || {};
|
|
997
977
|
const entries = inboxState?.entries || [];
|
|
998
978
|
const users = inboxState?.users || {};
|
|
999
|
-
const parsedUsers = Object.values(users).map(
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
})
|
|
1011
|
-
);
|
|
979
|
+
const parsedUsers = Object.values(users).map((user) => ({
|
|
980
|
+
id: user.id_str,
|
|
981
|
+
screenName: user.screen_name,
|
|
982
|
+
name: user.name,
|
|
983
|
+
profileImageUrl: user.profile_image_url_https,
|
|
984
|
+
description: user.description,
|
|
985
|
+
verified: user.verified,
|
|
986
|
+
protected: user.protected,
|
|
987
|
+
followersCount: user.followers_count,
|
|
988
|
+
friendsCount: user.friends_count
|
|
989
|
+
}));
|
|
1012
990
|
const messagesByConversation = {};
|
|
1013
991
|
entries.forEach((entry) => {
|
|
1014
992
|
if (entry.message) {
|
|
@@ -1175,11 +1153,7 @@ async function* getUserTimeline(query, maxProfiles, fetchFunc) {
|
|
|
1175
1153
|
let cursor = void 0;
|
|
1176
1154
|
let consecutiveEmptyBatches = 0;
|
|
1177
1155
|
while (nProfiles < maxProfiles) {
|
|
1178
|
-
const batch = await fetchFunc(
|
|
1179
|
-
query,
|
|
1180
|
-
maxProfiles,
|
|
1181
|
-
cursor
|
|
1182
|
-
);
|
|
1156
|
+
const batch = await fetchFunc(query, maxProfiles, cursor);
|
|
1183
1157
|
const { profiles, next } = batch;
|
|
1184
1158
|
cursor = next;
|
|
1185
1159
|
if (profiles.length === 0) {
|
|
@@ -1198,11 +1172,7 @@ async function* getTweetTimeline(query, maxTweets, fetchFunc) {
|
|
|
1198
1172
|
let nTweets = 0;
|
|
1199
1173
|
let cursor = void 0;
|
|
1200
1174
|
while (nTweets < maxTweets) {
|
|
1201
|
-
const batch = await fetchFunc(
|
|
1202
|
-
query,
|
|
1203
|
-
maxTweets,
|
|
1204
|
-
cursor
|
|
1205
|
-
);
|
|
1175
|
+
const batch = await fetchFunc(query, maxTweets, cursor);
|
|
1206
1176
|
const { tweets, next } = batch;
|
|
1207
1177
|
if (tweets.length === 0) {
|
|
1208
1178
|
break;
|
|
@@ -1241,10 +1211,7 @@ function parseRelationshipTimeline(timeline) {
|
|
|
1241
1211
|
if (itemContent?.userDisplayType === "User") {
|
|
1242
1212
|
const userResultRaw = itemContent.user_results?.result;
|
|
1243
1213
|
if (userResultRaw?.legacy) {
|
|
1244
|
-
const profile = parseProfile(
|
|
1245
|
-
userResultRaw.legacy,
|
|
1246
|
-
userResultRaw.is_blue_verified
|
|
1247
|
-
);
|
|
1214
|
+
const profile = parseProfile(userResultRaw.legacy, userResultRaw.is_blue_verified);
|
|
1248
1215
|
if (!profile.userId) {
|
|
1249
1216
|
profile.userId = userResultRaw.rest_id;
|
|
1250
1217
|
}
|
|
@@ -1273,21 +1240,11 @@ function getFollowers(userId, maxProfiles, auth) {
|
|
|
1273
1240
|
});
|
|
1274
1241
|
}
|
|
1275
1242
|
async function fetchProfileFollowing(userId, maxProfiles, auth, cursor) {
|
|
1276
|
-
const timeline = await getFollowingTimeline(
|
|
1277
|
-
userId,
|
|
1278
|
-
maxProfiles,
|
|
1279
|
-
auth,
|
|
1280
|
-
cursor
|
|
1281
|
-
);
|
|
1243
|
+
const timeline = await getFollowingTimeline(userId, maxProfiles, auth, cursor);
|
|
1282
1244
|
return parseRelationshipTimeline(timeline);
|
|
1283
1245
|
}
|
|
1284
1246
|
async function fetchProfileFollowers(userId, maxProfiles, auth, cursor) {
|
|
1285
|
-
const timeline = await getFollowersTimeline(
|
|
1286
|
-
userId,
|
|
1287
|
-
maxProfiles,
|
|
1288
|
-
auth,
|
|
1289
|
-
cursor
|
|
1290
|
-
);
|
|
1247
|
+
const timeline = await getFollowersTimeline(userId, maxProfiles, auth, cursor);
|
|
1291
1248
|
return parseRelationshipTimeline(timeline);
|
|
1292
1249
|
}
|
|
1293
1250
|
async function getFollowingTimeline(userId, maxItems, auth, cursor) {
|
|
@@ -1360,7 +1317,7 @@ async function followUser(username, auth) {
|
|
|
1360
1317
|
if (!await auth.isLoggedIn()) {
|
|
1361
1318
|
throw new Error("Must be logged in to follow users");
|
|
1362
1319
|
}
|
|
1363
|
-
const userIdResult = await
|
|
1320
|
+
const userIdResult = await getEntityIdByScreenName(username, auth);
|
|
1364
1321
|
if (!userIdResult.success) {
|
|
1365
1322
|
throw new Error(`Failed to get user ID: ${userIdResult.err.message}`);
|
|
1366
1323
|
}
|
|
@@ -1378,19 +1335,13 @@ async function followUser(username, auth) {
|
|
|
1378
1335
|
"X-Twitter-Client-Language": "en",
|
|
1379
1336
|
Authorization: `Bearer ${bearerToken}`
|
|
1380
1337
|
});
|
|
1381
|
-
await auth.installTo(
|
|
1338
|
+
await auth.installTo(headers, "https://api.twitter.com/1.1/friendships/create.json");
|
|
1339
|
+
const res = await auth.fetch("https://api.twitter.com/1.1/friendships/create.json", {
|
|
1340
|
+
method: "POST",
|
|
1382
1341
|
headers,
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
"https://api.twitter.com/1.1/friendships/create.json",
|
|
1387
|
-
{
|
|
1388
|
-
method: "POST",
|
|
1389
|
-
headers,
|
|
1390
|
-
body: new URLSearchParams(requestBody).toString(),
|
|
1391
|
-
credentials: "include"
|
|
1392
|
-
}
|
|
1393
|
-
);
|
|
1342
|
+
body: new URLSearchParams(requestBody).toString(),
|
|
1343
|
+
credentials: "include"
|
|
1344
|
+
});
|
|
1394
1345
|
if (!res.ok) {
|
|
1395
1346
|
throw new Error(`Failed to follow user: ${res.statusText}`);
|
|
1396
1347
|
}
|
|
@@ -1485,22 +1436,13 @@ function reconstructTweetHtml(tweet, photos, videos) {
|
|
|
1485
1436
|
return html;
|
|
1486
1437
|
}
|
|
1487
1438
|
function linkHashtagHtml(hashtag) {
|
|
1488
|
-
return `<a href="https://twitter.com/hashtag/${hashtag.replace(
|
|
1489
|
-
"#",
|
|
1490
|
-
""
|
|
1491
|
-
)}">${hashtag}</a>`;
|
|
1439
|
+
return `<a href="https://twitter.com/hashtag/${hashtag.replace("#", "")}">${hashtag}</a>`;
|
|
1492
1440
|
}
|
|
1493
1441
|
function linkCashtagHtml(cashtag) {
|
|
1494
|
-
return `<a href="https://twitter.com/search?q=%24${cashtag.replace(
|
|
1495
|
-
"$",
|
|
1496
|
-
""
|
|
1497
|
-
)}">${cashtag}</a>`;
|
|
1442
|
+
return `<a href="https://twitter.com/search?q=%24${cashtag.replace("$", "")}">${cashtag}</a>`;
|
|
1498
1443
|
}
|
|
1499
1444
|
function linkUsernameHtml(username) {
|
|
1500
|
-
return `<a href="https://twitter.com/${username.replace(
|
|
1501
|
-
"@",
|
|
1502
|
-
""
|
|
1503
|
-
)}">${username}</a>`;
|
|
1445
|
+
return `<a href="https://twitter.com/${username.replace("@", "")}">${username}</a>`;
|
|
1504
1446
|
}
|
|
1505
1447
|
function unwrapTcoUrlHtml(tweet, foundedMedia) {
|
|
1506
1448
|
return (tco) => {
|
|
@@ -1545,9 +1487,7 @@ function parseLegacyTweet(user, tweet) {
|
|
|
1545
1487
|
const hashtags = tweet.entities?.hashtags ?? [];
|
|
1546
1488
|
const mentions = tweet.entities?.user_mentions ?? [];
|
|
1547
1489
|
const media = tweet.extended_entities?.media ?? [];
|
|
1548
|
-
const pinnedTweets = new Set(
|
|
1549
|
-
user.pinned_tweet_ids_str ?? []
|
|
1550
|
-
);
|
|
1490
|
+
const pinnedTweets = new Set(user.pinned_tweet_ids_str ?? []);
|
|
1551
1491
|
const urls = tweet.entities?.urls ?? [];
|
|
1552
1492
|
const { photos, videos, sensitiveContent } = parseMediaGroups(media);
|
|
1553
1493
|
const tw = {
|
|
@@ -1628,10 +1568,7 @@ function parseResult(result) {
|
|
|
1628
1568
|
if (result?.legacy && noteTweetResultText) {
|
|
1629
1569
|
result.legacy.full_text = noteTweetResultText;
|
|
1630
1570
|
}
|
|
1631
|
-
const tweetResult = parseLegacyTweet(
|
|
1632
|
-
result?.core?.user_results?.result?.legacy,
|
|
1633
|
-
result?.legacy
|
|
1634
|
-
);
|
|
1571
|
+
const tweetResult = parseLegacyTweet(result?.core?.user_results?.result?.legacy, result?.legacy);
|
|
1635
1572
|
if (!tweetResult.success) {
|
|
1636
1573
|
return tweetResult;
|
|
1637
1574
|
}
|
|
@@ -1692,8 +1629,7 @@ function parseTimelineTweetsV2(timeline) {
|
|
|
1692
1629
|
function parseTimelineEntryItemContentRaw(content, entryId, isConversation = false) {
|
|
1693
1630
|
let result = content.tweet_results?.result ?? content.tweetResult?.result;
|
|
1694
1631
|
if (result?.__typename === "Tweet" || result?.__typename === "TweetWithVisibilityResults" && result?.tweet) {
|
|
1695
|
-
if (result?.__typename === "TweetWithVisibilityResults")
|
|
1696
|
-
result = result.tweet;
|
|
1632
|
+
if (result?.__typename === "TweetWithVisibilityResults") result = result.tweet;
|
|
1697
1633
|
if (result?.legacy) {
|
|
1698
1634
|
result.legacy.id_str = result.rest_id ?? entryId.replace("conversation-", "").replace("tweet-", "");
|
|
1699
1635
|
}
|
|
@@ -1710,11 +1646,7 @@ function parseTimelineEntryItemContentRaw(content, entryId, isConversation = fal
|
|
|
1710
1646
|
return null;
|
|
1711
1647
|
}
|
|
1712
1648
|
function parseAndPush(tweets, content, entryId, isConversation = false) {
|
|
1713
|
-
const tweet = parseTimelineEntryItemContentRaw(
|
|
1714
|
-
content,
|
|
1715
|
-
entryId,
|
|
1716
|
-
isConversation
|
|
1717
|
-
);
|
|
1649
|
+
const tweet = parseTimelineEntryItemContentRaw(content, entryId, isConversation);
|
|
1718
1650
|
if (tweet) {
|
|
1719
1651
|
tweets.push(tweet);
|
|
1720
1652
|
}
|
|
@@ -1845,10 +1777,7 @@ function parseSearchTimelineUsers(timeline) {
|
|
|
1845
1777
|
if (itemContent?.userDisplayType === "User") {
|
|
1846
1778
|
const userResultRaw = itemContent.user_results?.result;
|
|
1847
1779
|
if (userResultRaw?.legacy) {
|
|
1848
|
-
const profile = parseProfile(
|
|
1849
|
-
userResultRaw.legacy,
|
|
1850
|
-
userResultRaw.is_blue_verified
|
|
1851
|
-
);
|
|
1780
|
+
const profile = parseProfile(userResultRaw.legacy, userResultRaw.is_blue_verified);
|
|
1852
1781
|
if (!profile.userId) {
|
|
1853
1782
|
profile.userId = userResultRaw.rest_id;
|
|
1854
1783
|
}
|
|
@@ -1877,23 +1806,11 @@ function searchProfiles(query, maxProfiles, auth) {
|
|
|
1877
1806
|
});
|
|
1878
1807
|
}
|
|
1879
1808
|
async function fetchSearchTweets(query, maxTweets, searchMode, auth, cursor) {
|
|
1880
|
-
const timeline = await getSearchTimeline(
|
|
1881
|
-
query,
|
|
1882
|
-
maxTweets,
|
|
1883
|
-
searchMode,
|
|
1884
|
-
auth,
|
|
1885
|
-
cursor
|
|
1886
|
-
);
|
|
1809
|
+
const timeline = await getSearchTimeline(query, maxTweets, searchMode, auth, cursor);
|
|
1887
1810
|
return parseSearchTimelineTweets(timeline);
|
|
1888
1811
|
}
|
|
1889
1812
|
async function fetchSearchProfiles(query, maxProfiles, auth, cursor) {
|
|
1890
|
-
const timeline = await getSearchTimeline(
|
|
1891
|
-
query,
|
|
1892
|
-
maxProfiles,
|
|
1893
|
-
4 /* Users */,
|
|
1894
|
-
auth,
|
|
1895
|
-
cursor
|
|
1896
|
-
);
|
|
1813
|
+
const timeline = await getSearchTimeline(query, maxProfiles, 4 /* Users */, auth, cursor);
|
|
1897
1814
|
return parseSearchTimelineUsers(timeline);
|
|
1898
1815
|
}
|
|
1899
1816
|
async function getSearchTimeline(query, maxItems, searchMode, auth, cursor) {
|
|
@@ -2121,9 +2038,7 @@ async function fetchBrowseSpaceTopics(auth) {
|
|
|
2121
2038
|
if (data.errors && data.errors.length > 0) {
|
|
2122
2039
|
throw new Error(`API Errors: ${JSON.stringify(data.errors)}`);
|
|
2123
2040
|
}
|
|
2124
|
-
return data.data.browse_space_topics.categories.flatMap(
|
|
2125
|
-
(category) => category.subtopics
|
|
2126
|
-
);
|
|
2041
|
+
return data.data.browse_space_topics.categories.flatMap((category) => category.subtopics);
|
|
2127
2042
|
}
|
|
2128
2043
|
async function fetchCommunitySelectQuery(auth) {
|
|
2129
2044
|
const queryId = "Lue1DfmoW2cc0225t_8z1w";
|
|
@@ -2153,9 +2068,7 @@ async function fetchCommunitySelectQuery(auth) {
|
|
|
2153
2068
|
});
|
|
2154
2069
|
await updateCookieJar(auth.cookieJar(), response.headers);
|
|
2155
2070
|
if (!response.ok) {
|
|
2156
|
-
throw new Error(
|
|
2157
|
-
`Failed to fetch Community Select Query: ${await response.text()}`
|
|
2158
|
-
);
|
|
2071
|
+
throw new Error(`Failed to fetch Community Select Query: ${await response.text()}`);
|
|
2159
2072
|
}
|
|
2160
2073
|
const data = await response.json();
|
|
2161
2074
|
if (data.errors && data.errors.length > 0) {
|
|
@@ -2192,16 +2105,11 @@ async function fetchLiveVideoStreamStatus(mediaKey, auth) {
|
|
|
2192
2105
|
});
|
|
2193
2106
|
await updateCookieJar(auth.cookieJar(), response.headers);
|
|
2194
2107
|
if (!response.ok) {
|
|
2195
|
-
throw new Error(
|
|
2196
|
-
`Failed to fetch live video stream status: ${await response.text()}`
|
|
2197
|
-
);
|
|
2108
|
+
throw new Error(`Failed to fetch live video stream status: ${await response.text()}`);
|
|
2198
2109
|
}
|
|
2199
2110
|
return await response.json();
|
|
2200
2111
|
} catch (error) {
|
|
2201
|
-
console.error(
|
|
2202
|
-
`Error fetching live video stream status for mediaKey ${mediaKey}:`,
|
|
2203
|
-
error
|
|
2204
|
-
);
|
|
2112
|
+
console.error(`Error fetching live video stream status for mediaKey ${mediaKey}:`, error);
|
|
2205
2113
|
throw error;
|
|
2206
2114
|
}
|
|
2207
2115
|
}
|
|
@@ -2343,7 +2251,6 @@ async function fetchFollowingTimeline(count, seenTweetIds, auth) {
|
|
|
2343
2251
|
auth,
|
|
2344
2252
|
"GET"
|
|
2345
2253
|
);
|
|
2346
|
-
console.log("res", res);
|
|
2347
2254
|
if (!res.success) {
|
|
2348
2255
|
if (res.err instanceof ApiError) {
|
|
2349
2256
|
console.error("Error details:", res.err.data);
|
|
@@ -2477,10 +2384,6 @@ var endpoints = {
|
|
|
2477
2384
|
ListTweets: "https://twitter.com/i/api/graphql/whF0_KH1fCkdLLoyNPMoEw/ListLatestTweetsTimeline?variables=%7B%22listId%22%3A%221736495155002106192%22%2C%22count%22%3A20%7D&features=%7B%22responsive_web_graphql_exclude_directive_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Afalse%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22rweb_video_timestamps_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_media_download_video_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D"
|
|
2478
2385
|
};
|
|
2479
2386
|
var ApiRequest = class {
|
|
2480
|
-
url;
|
|
2481
|
-
variables;
|
|
2482
|
-
features;
|
|
2483
|
-
fieldToggles;
|
|
2484
2387
|
constructor(info) {
|
|
2485
2388
|
this.url = info.url;
|
|
2486
2389
|
this.variables = info.variables;
|
|
@@ -2665,10 +2568,7 @@ async function fetchTweets(userId, maxTweets, cursor, auth) {
|
|
|
2665
2568
|
if (cursor != null && cursor !== "") {
|
|
2666
2569
|
userTweetsRequest.variables.cursor = cursor;
|
|
2667
2570
|
}
|
|
2668
|
-
const res = await requestApi(
|
|
2669
|
-
userTweetsRequest.toRequestUrl(),
|
|
2670
|
-
auth
|
|
2671
|
-
);
|
|
2571
|
+
const res = await requestApi(userTweetsRequest.toRequestUrl(), auth);
|
|
2672
2572
|
if (!res.success) {
|
|
2673
2573
|
throw res.err;
|
|
2674
2574
|
}
|
|
@@ -2685,10 +2585,7 @@ async function fetchTweetsAndReplies(userId, maxTweets, cursor, auth) {
|
|
|
2685
2585
|
if (cursor != null && cursor !== "") {
|
|
2686
2586
|
userTweetsRequest.variables.cursor = cursor;
|
|
2687
2587
|
}
|
|
2688
|
-
const res = await requestApi(
|
|
2689
|
-
userTweetsRequest.toRequestUrl(),
|
|
2690
|
-
auth
|
|
2691
|
-
);
|
|
2588
|
+
const res = await requestApi(userTweetsRequest.toRequestUrl(), auth);
|
|
2692
2589
|
if (!res.success) {
|
|
2693
2590
|
throw res.err;
|
|
2694
2591
|
}
|
|
@@ -2726,12 +2623,7 @@ async function createCreateTweetRequestV2(text, auth, tweetId, options) {
|
|
|
2726
2623
|
if (options?.poll) {
|
|
2727
2624
|
optionsConfig = {
|
|
2728
2625
|
expansions: ["attachments.poll_ids"],
|
|
2729
|
-
pollFields: [
|
|
2730
|
-
"options",
|
|
2731
|
-
"duration_minutes",
|
|
2732
|
-
"end_datetime",
|
|
2733
|
-
"voting_status"
|
|
2734
|
-
]
|
|
2626
|
+
pollFields: ["options", "duration_minutes", "end_datetime", "voting_status"]
|
|
2735
2627
|
};
|
|
2736
2628
|
}
|
|
2737
2629
|
return await getTweetV2(tweetResponse.data.id, auth, optionsConfig);
|
|
@@ -2789,26 +2681,20 @@ function parseTweetV2ToV1(tweetV2, includes, defaultTweetData) {
|
|
|
2789
2681
|
parsedTweet.videos.push({
|
|
2790
2682
|
id: media.media_key,
|
|
2791
2683
|
preview: media.preview_image_url ?? "",
|
|
2792
|
-
url: media.variants?.find(
|
|
2793
|
-
(variant) => variant.content_type === "video/mp4"
|
|
2794
|
-
)?.url ?? ""
|
|
2684
|
+
url: media.variants?.find((variant) => variant.content_type === "video/mp4")?.url ?? ""
|
|
2795
2685
|
});
|
|
2796
2686
|
}
|
|
2797
2687
|
});
|
|
2798
2688
|
}
|
|
2799
2689
|
if (includes?.users?.length) {
|
|
2800
|
-
const user = includes.users.find(
|
|
2801
|
-
(user2) => user2.id === tweetV2.author_id
|
|
2802
|
-
);
|
|
2690
|
+
const user = includes.users.find((user2) => user2.id === tweetV2.author_id);
|
|
2803
2691
|
if (user) {
|
|
2804
2692
|
parsedTweet.username = user.username ?? defaultTweetData?.username ?? "";
|
|
2805
2693
|
parsedTweet.name = user.name ?? defaultTweetData?.name ?? "";
|
|
2806
2694
|
}
|
|
2807
2695
|
}
|
|
2808
2696
|
if (tweetV2?.geo?.place_id && includes?.places?.length) {
|
|
2809
|
-
const place = includes.places.find(
|
|
2810
|
-
(place2) => place2.id === tweetV2?.geo?.place_id
|
|
2811
|
-
);
|
|
2697
|
+
const place = includes.places.find((place2) => place2.id === tweetV2?.geo?.place_id);
|
|
2812
2698
|
if (place) {
|
|
2813
2699
|
parsedTweet.place = {
|
|
2814
2700
|
id: place.id,
|
|
@@ -2851,9 +2737,7 @@ async function createCreateTweetRequest(text, auth, tweetId, mediaData, hideLink
|
|
|
2851
2737
|
}
|
|
2852
2738
|
if (mediaData && mediaData.length > 0) {
|
|
2853
2739
|
const mediaIds = await Promise.all(
|
|
2854
|
-
mediaData.map(
|
|
2855
|
-
({ data, mediaType }) => uploadMedia(data, auth, mediaType)
|
|
2856
|
-
)
|
|
2740
|
+
mediaData.map(({ data, mediaType }) => uploadMedia(data, auth, mediaType))
|
|
2857
2741
|
);
|
|
2858
2742
|
variables.media.media_entities = mediaIds.map((id) => ({
|
|
2859
2743
|
media_id: id,
|
|
@@ -2943,9 +2827,7 @@ async function createCreateNoteTweetRequest(text, auth, tweetId, mediaData) {
|
|
|
2943
2827
|
};
|
|
2944
2828
|
if (mediaData && mediaData.length > 0) {
|
|
2945
2829
|
const mediaIds = await Promise.all(
|
|
2946
|
-
mediaData.map(
|
|
2947
|
-
({ data: data2, mediaType }) => uploadMedia(data2, auth, mediaType)
|
|
2948
|
-
)
|
|
2830
|
+
mediaData.map(({ data: data2, mediaType }) => uploadMedia(data2, auth, mediaType))
|
|
2949
2831
|
);
|
|
2950
2832
|
variables.media.media_entities = mediaIds.map((id) => ({
|
|
2951
2833
|
media_id: id,
|
|
@@ -3027,10 +2909,7 @@ async function fetchListTweets(listId, maxTweets, cursor, auth) {
|
|
|
3027
2909
|
if (cursor != null && cursor !== "") {
|
|
3028
2910
|
listTweetsRequest.variables.cursor = cursor;
|
|
3029
2911
|
}
|
|
3030
|
-
const res = await requestApi(
|
|
3031
|
-
listTweetsRequest.toRequestUrl(),
|
|
3032
|
-
auth
|
|
3033
|
-
);
|
|
2912
|
+
const res = await requestApi(listTweetsRequest.toRequestUrl(), auth);
|
|
3034
2913
|
if (!res.success) {
|
|
3035
2914
|
throw res.err;
|
|
3036
2915
|
}
|
|
@@ -3038,7 +2917,7 @@ async function fetchListTweets(listId, maxTweets, cursor, auth) {
|
|
|
3038
2917
|
}
|
|
3039
2918
|
function getTweets(user, maxTweets, auth) {
|
|
3040
2919
|
return getTweetTimeline(user, maxTweets, async (q, mt, c) => {
|
|
3041
|
-
const userIdRes = await
|
|
2920
|
+
const userIdRes = await getEntityIdByScreenName(q, auth);
|
|
3042
2921
|
if (!userIdRes.success) {
|
|
3043
2922
|
throw userIdRes.err;
|
|
3044
2923
|
}
|
|
@@ -3053,7 +2932,7 @@ function getTweetsByUserId(userId, maxTweets, auth) {
|
|
|
3053
2932
|
}
|
|
3054
2933
|
function getTweetsAndReplies(user, maxTweets, auth) {
|
|
3055
2934
|
return getTweetTimeline(user, maxTweets, async (q, mt, c) => {
|
|
3056
|
-
const userIdRes = await
|
|
2935
|
+
const userIdRes = await getEntityIdByScreenName(q, auth);
|
|
3057
2936
|
if (!userIdRes.success) {
|
|
3058
2937
|
throw userIdRes.err;
|
|
3059
2938
|
}
|
|
@@ -3099,10 +2978,7 @@ async function getLatestTweet(user, includeRetweets, max, auth) {
|
|
|
3099
2978
|
async function getTweet(id, auth) {
|
|
3100
2979
|
const tweetDetailRequest = apiRequestFactory.createTweetDetailRequest();
|
|
3101
2980
|
tweetDetailRequest.variables.focalTweetId = id;
|
|
3102
|
-
const res = await requestApi(
|
|
3103
|
-
tweetDetailRequest.toRequestUrl(),
|
|
3104
|
-
auth
|
|
3105
|
-
);
|
|
2981
|
+
const res = await requestApi(tweetDetailRequest.toRequestUrl(), auth);
|
|
3106
2982
|
if (!res.success) {
|
|
3107
2983
|
throw res.err;
|
|
3108
2984
|
}
|
|
@@ -3131,11 +3007,7 @@ async function getTweetV2(id, auth, options = defaultOptions) {
|
|
|
3131
3007
|
return null;
|
|
3132
3008
|
}
|
|
3133
3009
|
const defaultTweetData = await getTweet(tweetData.data.id, auth);
|
|
3134
|
-
const parsedTweet = parseTweetV2ToV1(
|
|
3135
|
-
tweetData.data,
|
|
3136
|
-
tweetData?.includes,
|
|
3137
|
-
defaultTweetData
|
|
3138
|
-
);
|
|
3010
|
+
const parsedTweet = parseTweetV2ToV1(tweetData.data, tweetData?.includes, defaultTweetData);
|
|
3139
3011
|
return parsedTweet;
|
|
3140
3012
|
} catch (error) {
|
|
3141
3013
|
console.error(`Error fetching tweet ${id}:`, error);
|
|
@@ -3161,11 +3033,7 @@ async function getTweetsV2(ids, auth, options = defaultOptions) {
|
|
|
3161
3033
|
console.warn(`No tweet data found for IDs: ${ids.join(", ")}`);
|
|
3162
3034
|
return [];
|
|
3163
3035
|
}
|
|
3164
|
-
return (await Promise.all(
|
|
3165
|
-
tweetsV2.map(
|
|
3166
|
-
async (tweet) => await getTweetV2(tweet.id, auth, options)
|
|
3167
|
-
)
|
|
3168
|
-
)).filter((tweet) => tweet !== null);
|
|
3036
|
+
return (await Promise.all(tweetsV2.map(async (tweet) => await getTweetV2(tweet.id, auth, options)))).filter((tweet) => tweet !== null);
|
|
3169
3037
|
} catch (error) {
|
|
3170
3038
|
console.error(`Error fetching tweets for IDs: ${ids.join(", ")}`, error);
|
|
3171
3039
|
return [];
|
|
@@ -3271,13 +3139,10 @@ async function uploadMedia(mediaData, auth, mediaType) {
|
|
|
3271
3139
|
const statusParams = new URLSearchParams();
|
|
3272
3140
|
statusParams.append("command", "STATUS");
|
|
3273
3141
|
statusParams.append("media_id", mediaId);
|
|
3274
|
-
const statusResponse = await fetch(
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
headers
|
|
3279
|
-
}
|
|
3280
|
-
);
|
|
3142
|
+
const statusResponse = await fetch(`${uploadUrl}?${statusParams.toString()}`, {
|
|
3143
|
+
method: "GET",
|
|
3144
|
+
headers
|
|
3145
|
+
});
|
|
3281
3146
|
if (!statusResponse.ok) {
|
|
3282
3147
|
throw new Error(await statusResponse.text());
|
|
3283
3148
|
}
|
|
@@ -3317,9 +3182,7 @@ async function createQuoteTweetRequest(text, quotedTweetId, auth, mediaData) {
|
|
|
3317
3182
|
};
|
|
3318
3183
|
if (mediaData && mediaData.length > 0) {
|
|
3319
3184
|
const mediaIds = await Promise.all(
|
|
3320
|
-
mediaData.map(
|
|
3321
|
-
({ data, mediaType }) => uploadMedia(data, auth, mediaType)
|
|
3322
|
-
)
|
|
3185
|
+
mediaData.map(({ data, mediaType }) => uploadMedia(data, auth, mediaType))
|
|
3323
3186
|
);
|
|
3324
3187
|
variables.media.media_entities = mediaIds.map((id) => ({
|
|
3325
3188
|
media_id: id,
|
|
@@ -3464,9 +3327,7 @@ async function createCreateLongTweetRequest(text, auth, tweetId, mediaData) {
|
|
|
3464
3327
|
};
|
|
3465
3328
|
if (mediaData && mediaData.length > 0) {
|
|
3466
3329
|
const mediaIds = await Promise.all(
|
|
3467
|
-
mediaData.map(
|
|
3468
|
-
({ data, mediaType }) => uploadMedia(data, auth, mediaType)
|
|
3469
|
-
)
|
|
3330
|
+
mediaData.map(({ data, mediaType }) => uploadMedia(data, auth, mediaType))
|
|
3470
3331
|
);
|
|
3471
3332
|
variables.media.media_entities = mediaIds.map((id) => ({
|
|
3472
3333
|
media_id: id,
|
|
@@ -3521,10 +3382,7 @@ async function createCreateLongTweetRequest(text, auth, tweetId, mediaData) {
|
|
|
3521
3382
|
async function getArticle(id, auth) {
|
|
3522
3383
|
const tweetDetailRequest = apiRequestFactory.createTweetDetailArticleRequest();
|
|
3523
3384
|
tweetDetailRequest.variables.focalTweetId = id;
|
|
3524
|
-
const res = await requestApi(
|
|
3525
|
-
tweetDetailRequest.toRequestUrl(),
|
|
3526
|
-
auth
|
|
3527
|
-
);
|
|
3385
|
+
const res = await requestApi(tweetDetailRequest.toRequestUrl(), auth);
|
|
3528
3386
|
if (!res.success) {
|
|
3529
3387
|
throw res.err;
|
|
3530
3388
|
}
|
|
@@ -3659,9 +3517,6 @@ var Client = class {
|
|
|
3659
3517
|
this.token = bearerToken;
|
|
3660
3518
|
this.useGuestAuth();
|
|
3661
3519
|
}
|
|
3662
|
-
auth;
|
|
3663
|
-
authTrends;
|
|
3664
|
-
token;
|
|
3665
3520
|
/**
|
|
3666
3521
|
* Initializes auth properties using a guest token.
|
|
3667
3522
|
* Used when creating a new instance of this class, and when logging out.
|
|
@@ -3685,8 +3540,8 @@ var Client = class {
|
|
|
3685
3540
|
* @param screenName The Twitter screen name of the profile to fetch.
|
|
3686
3541
|
* @returns The ID of the corresponding account.
|
|
3687
3542
|
*/
|
|
3688
|
-
async
|
|
3689
|
-
const res = await
|
|
3543
|
+
async getEntityIdByScreenName(screenName) {
|
|
3544
|
+
const res = await getEntityIdByScreenName(screenName, this.auth);
|
|
3690
3545
|
return this.handleResponse(res);
|
|
3691
3546
|
}
|
|
3692
3547
|
/**
|
|
@@ -3870,11 +3725,7 @@ var Client = class {
|
|
|
3870
3725
|
let cursor;
|
|
3871
3726
|
let retrievedTweets = 0;
|
|
3872
3727
|
while (retrievedTweets < maxTweets) {
|
|
3873
|
-
const response = await this.getUserTweets(
|
|
3874
|
-
userId,
|
|
3875
|
-
maxTweets - retrievedTweets,
|
|
3876
|
-
cursor
|
|
3877
|
-
);
|
|
3728
|
+
const response = await this.getUserTweets(userId, maxTweets - retrievedTweets, cursor);
|
|
3878
3729
|
for (const tweet of response.tweets) {
|
|
3879
3730
|
yield tweet;
|
|
3880
3731
|
retrievedTweets++;
|
|
@@ -3930,12 +3781,7 @@ var Client = class {
|
|
|
3930
3781
|
);
|
|
3931
3782
|
}
|
|
3932
3783
|
async sendNoteTweet(text, replyToTweetId, mediaData) {
|
|
3933
|
-
return await createCreateNoteTweetRequest(
|
|
3934
|
-
text,
|
|
3935
|
-
this.auth,
|
|
3936
|
-
replyToTweetId,
|
|
3937
|
-
mediaData
|
|
3938
|
-
);
|
|
3784
|
+
return await createCreateNoteTweetRequest(text, this.auth, replyToTweetId, mediaData);
|
|
3939
3785
|
}
|
|
3940
3786
|
/**
|
|
3941
3787
|
* Send a long tweet (Note Tweet)
|
|
@@ -3945,12 +3791,7 @@ var Client = class {
|
|
|
3945
3791
|
* @returns
|
|
3946
3792
|
*/
|
|
3947
3793
|
async sendLongTweet(text, replyToTweetId, mediaData) {
|
|
3948
|
-
return await createCreateLongTweetRequest(
|
|
3949
|
-
text,
|
|
3950
|
-
this.auth,
|
|
3951
|
-
replyToTweetId,
|
|
3952
|
-
mediaData
|
|
3953
|
-
);
|
|
3794
|
+
return await createCreateLongTweetRequest(text, this.auth, replyToTweetId, mediaData);
|
|
3954
3795
|
}
|
|
3955
3796
|
/**
|
|
3956
3797
|
* Send a tweet
|
|
@@ -3960,12 +3801,7 @@ var Client = class {
|
|
|
3960
3801
|
* @returns
|
|
3961
3802
|
*/
|
|
3962
3803
|
async sendTweetV2(text, replyToTweetId, options) {
|
|
3963
|
-
return await createCreateTweetRequestV2(
|
|
3964
|
-
text,
|
|
3965
|
-
this.auth,
|
|
3966
|
-
replyToTweetId,
|
|
3967
|
-
options
|
|
3968
|
-
);
|
|
3804
|
+
return await createCreateTweetRequestV2(text, this.auth, replyToTweetId, options);
|
|
3969
3805
|
}
|
|
3970
3806
|
/**
|
|
3971
3807
|
* Fetches tweets and replies from a Twitter user.
|
|
@@ -4134,9 +3970,7 @@ var Client = class {
|
|
|
4134
3970
|
* @returns All cookies for the current session.
|
|
4135
3971
|
*/
|
|
4136
3972
|
async getCookies() {
|
|
4137
|
-
return await this.authTrends.cookieJar().getCookies(
|
|
4138
|
-
typeof document !== "undefined" ? document.location.toString() : twUrl
|
|
4139
|
-
);
|
|
3973
|
+
return await this.authTrends.cookieJar().getCookies(typeof document !== "undefined" ? document.location.toString() : twUrl);
|
|
4140
3974
|
}
|
|
4141
3975
|
/**
|
|
4142
3976
|
* Set cookies for the current session.
|
|
@@ -4191,12 +4025,7 @@ var Client = class {
|
|
|
4191
4025
|
* @returns The response from the Twitter API.
|
|
4192
4026
|
*/
|
|
4193
4027
|
async sendQuoteTweet(text, quotedTweetId, options) {
|
|
4194
|
-
return await createQuoteTweetRequest(
|
|
4195
|
-
text,
|
|
4196
|
-
quotedTweetId,
|
|
4197
|
-
this.auth,
|
|
4198
|
-
options?.mediaData
|
|
4199
|
-
);
|
|
4028
|
+
return await createQuoteTweetRequest(text, quotedTweetId, this.auth, options?.mediaData);
|
|
4200
4029
|
}
|
|
4201
4030
|
/**
|
|
4202
4031
|
* Likes a tweet with the given tweet ID.
|
|
@@ -4373,12 +4202,7 @@ var Client = class {
|
|
|
4373
4202
|
let cursor;
|
|
4374
4203
|
let prevCursor;
|
|
4375
4204
|
while (true) {
|
|
4376
|
-
const page = await fetchQuotedTweetsPage(
|
|
4377
|
-
quotedTweetId,
|
|
4378
|
-
maxTweetsPerPage,
|
|
4379
|
-
this.auth,
|
|
4380
|
-
cursor
|
|
4381
|
-
);
|
|
4205
|
+
const page = await fetchQuotedTweetsPage(quotedTweetId, maxTweetsPerPage, this.auth, cursor);
|
|
4382
4206
|
if (!page.tweets || page.tweets.length === 0) {
|
|
4383
4207
|
break;
|
|
4384
4208
|
}
|
|
@@ -4398,7 +4222,6 @@ import { EventEmitter as EventEmitter4 } from "node:events";
|
|
|
4398
4222
|
|
|
4399
4223
|
// src/client/spaces/logger.ts
|
|
4400
4224
|
var Logger = class {
|
|
4401
|
-
debugEnabled;
|
|
4402
4225
|
/**
|
|
4403
4226
|
* Constructor for initializing a new instance of the class.
|
|
4404
4227
|
*
|
|
@@ -4473,15 +4296,11 @@ async function authorizeToken(cookie) {
|
|
|
4473
4296
|
})
|
|
4474
4297
|
});
|
|
4475
4298
|
if (!resp.ok) {
|
|
4476
|
-
throw new Error(
|
|
4477
|
-
`authorizeToken => request failed with status ${resp.status}`
|
|
4478
|
-
);
|
|
4299
|
+
throw new Error(`authorizeToken => request failed with status ${resp.status}`);
|
|
4479
4300
|
}
|
|
4480
4301
|
const data = await resp.json();
|
|
4481
4302
|
if (!data.authorization_token) {
|
|
4482
|
-
throw new Error(
|
|
4483
|
-
"authorizeToken => Missing authorization_token in response"
|
|
4484
|
-
);
|
|
4303
|
+
throw new Error("authorizeToken => Missing authorization_token in response");
|
|
4485
4304
|
}
|
|
4486
4305
|
return data.authorization_token;
|
|
4487
4306
|
}
|
|
@@ -4523,9 +4342,7 @@ async function getTurnServers(cookie) {
|
|
|
4523
4342
|
body: JSON.stringify({ cookie })
|
|
4524
4343
|
});
|
|
4525
4344
|
if (!resp.ok) {
|
|
4526
|
-
throw new Error(
|
|
4527
|
-
`getTurnServers => request failed with status ${resp.status}`
|
|
4528
|
-
);
|
|
4345
|
+
throw new Error(`getTurnServers => request failed with status ${resp.status}`);
|
|
4529
4346
|
}
|
|
4530
4347
|
return resp.json();
|
|
4531
4348
|
}
|
|
@@ -4572,9 +4389,7 @@ async function createBroadcast(params) {
|
|
|
4572
4389
|
});
|
|
4573
4390
|
if (!resp.ok) {
|
|
4574
4391
|
const text = await resp.text();
|
|
4575
|
-
throw new Error(
|
|
4576
|
-
`createBroadcast => request failed with status ${resp.status} ${text}`
|
|
4577
|
-
);
|
|
4392
|
+
throw new Error(`createBroadcast => request failed with status ${resp.status} ${text}`);
|
|
4578
4393
|
}
|
|
4579
4394
|
const data = await resp.json();
|
|
4580
4395
|
return data;
|
|
@@ -4616,9 +4431,7 @@ async function startWatching(lifecycleToken, cookie) {
|
|
|
4616
4431
|
body: JSON.stringify(body)
|
|
4617
4432
|
});
|
|
4618
4433
|
if (!resp.ok) {
|
|
4619
|
-
throw new Error(
|
|
4620
|
-
`startWatching => request failed with status ${resp.status}`
|
|
4621
|
-
);
|
|
4434
|
+
throw new Error(`startWatching => request failed with status ${resp.status}`);
|
|
4622
4435
|
}
|
|
4623
4436
|
const json = await resp.json();
|
|
4624
4437
|
return json.session;
|
|
@@ -4636,9 +4449,7 @@ async function stopWatching(session, cookie) {
|
|
|
4636
4449
|
body: JSON.stringify(body)
|
|
4637
4450
|
});
|
|
4638
4451
|
if (!resp.ok) {
|
|
4639
|
-
throw new Error(
|
|
4640
|
-
`stopWatching => request failed with status ${resp.status}`
|
|
4641
|
-
);
|
|
4452
|
+
throw new Error(`stopWatching => request failed with status ${resp.status}`);
|
|
4642
4453
|
}
|
|
4643
4454
|
}
|
|
4644
4455
|
async function submitSpeakerRequest(params) {
|
|
@@ -4659,9 +4470,7 @@ async function submitSpeakerRequest(params) {
|
|
|
4659
4470
|
body: JSON.stringify(body)
|
|
4660
4471
|
});
|
|
4661
4472
|
if (!resp.ok) {
|
|
4662
|
-
throw new Error(
|
|
4663
|
-
`submitSpeakerRequest => request failed with status ${resp.status}`
|
|
4664
|
-
);
|
|
4473
|
+
throw new Error(`submitSpeakerRequest => request failed with status ${resp.status}`);
|
|
4665
4474
|
}
|
|
4666
4475
|
return resp.json();
|
|
4667
4476
|
}
|
|
@@ -4684,9 +4493,7 @@ async function cancelSpeakerRequest(params) {
|
|
|
4684
4493
|
body: JSON.stringify(body)
|
|
4685
4494
|
});
|
|
4686
4495
|
if (!resp.ok) {
|
|
4687
|
-
throw new Error(
|
|
4688
|
-
`cancelSpeakerRequest => request failed with status ${resp.status}`
|
|
4689
|
-
);
|
|
4496
|
+
throw new Error(`cancelSpeakerRequest => request failed with status ${resp.status}`);
|
|
4690
4497
|
}
|
|
4691
4498
|
return resp.json();
|
|
4692
4499
|
}
|
|
@@ -4705,9 +4512,7 @@ async function negotiateGuestStream(params) {
|
|
|
4705
4512
|
body: JSON.stringify(body)
|
|
4706
4513
|
});
|
|
4707
4514
|
if (!resp.ok) {
|
|
4708
|
-
throw new Error(
|
|
4709
|
-
`negotiateGuestStream => request failed with status ${resp.status}`
|
|
4710
|
-
);
|
|
4515
|
+
throw new Error(`negotiateGuestStream => request failed with status ${resp.status}`);
|
|
4711
4516
|
}
|
|
4712
4517
|
return resp.json();
|
|
4713
4518
|
}
|
|
@@ -4757,29 +4562,29 @@ async function unmuteSpeaker(params) {
|
|
|
4757
4562
|
throw new Error(`unmuteSpeaker => ${resp.status} ${text}`);
|
|
4758
4563
|
}
|
|
4759
4564
|
}
|
|
4760
|
-
function setupCommonChatEvents(chatClient,
|
|
4565
|
+
function setupCommonChatEvents(chatClient, logger11, emitter) {
|
|
4761
4566
|
chatClient.on("occupancyUpdate", (upd) => {
|
|
4762
|
-
|
|
4567
|
+
logger11.debug("[ChatEvents] occupancyUpdate =>", upd);
|
|
4763
4568
|
emitter.emit("occupancyUpdate", upd);
|
|
4764
4569
|
});
|
|
4765
4570
|
chatClient.on("guestReaction", (reaction) => {
|
|
4766
|
-
|
|
4571
|
+
logger11.debug("[ChatEvents] guestReaction =>", reaction);
|
|
4767
4572
|
emitter.emit("guestReaction", reaction);
|
|
4768
4573
|
});
|
|
4769
4574
|
chatClient.on("muteStateChanged", (evt) => {
|
|
4770
|
-
|
|
4575
|
+
logger11.debug("[ChatEvents] muteStateChanged =>", evt);
|
|
4771
4576
|
emitter.emit("muteStateChanged", evt);
|
|
4772
4577
|
});
|
|
4773
4578
|
chatClient.on("speakerRequest", (req) => {
|
|
4774
|
-
|
|
4579
|
+
logger11.debug("[ChatEvents] speakerRequest =>", req);
|
|
4775
4580
|
emitter.emit("speakerRequest", req);
|
|
4776
4581
|
});
|
|
4777
4582
|
chatClient.on("newSpeakerAccepted", (info) => {
|
|
4778
|
-
|
|
4583
|
+
logger11.debug("[ChatEvents] newSpeakerAccepted =>", info);
|
|
4779
4584
|
emitter.emit("newSpeakerAccepted", info);
|
|
4780
4585
|
});
|
|
4781
4586
|
chatClient.on("newSpeakerRemoved", (info) => {
|
|
4782
|
-
|
|
4587
|
+
logger11.debug("[ChatEvents] newSpeakerRemoved =>", info);
|
|
4783
4588
|
emitter.emit("newSpeakerRemoved", info);
|
|
4784
4589
|
});
|
|
4785
4590
|
}
|
|
@@ -4788,14 +4593,9 @@ function setupCommonChatEvents(chatClient, logger10, emitter) {
|
|
|
4788
4593
|
import { EventEmitter } from "node:events";
|
|
4789
4594
|
import WebSocket from "ws";
|
|
4790
4595
|
var ChatClient = class extends EventEmitter {
|
|
4791
|
-
ws;
|
|
4792
|
-
connected = false;
|
|
4793
|
-
logger;
|
|
4794
|
-
spaceId;
|
|
4795
|
-
accessToken;
|
|
4796
|
-
endpoint;
|
|
4797
4596
|
constructor(config) {
|
|
4798
4597
|
super();
|
|
4598
|
+
this.connected = false;
|
|
4799
4599
|
this.spaceId = config.spaceId;
|
|
4800
4600
|
this.accessToken = config.accessToken;
|
|
4801
4601
|
this.endpoint = config.endpoint;
|
|
@@ -4805,10 +4605,7 @@ var ChatClient = class extends EventEmitter {
|
|
|
4805
4605
|
* Establishes a WebSocket connection to the chat endpoint and sets up event handlers.
|
|
4806
4606
|
*/
|
|
4807
4607
|
async connect() {
|
|
4808
|
-
const wsUrl = `${this.endpoint}/chatapi/v1/chatnow`.replace(
|
|
4809
|
-
"https://",
|
|
4810
|
-
"wss://"
|
|
4811
|
-
);
|
|
4608
|
+
const wsUrl = `${this.endpoint}/chatapi/v1/chatnow`.replace("https://", "wss://");
|
|
4812
4609
|
this.logger.info("[ChatClient] Connecting =>", wsUrl);
|
|
4813
4610
|
this.ws = new WebSocket(wsUrl, {
|
|
4814
4611
|
headers: {
|
|
@@ -4873,24 +4670,22 @@ var ChatClient = class extends EventEmitter {
|
|
|
4873
4670
|
*/
|
|
4874
4671
|
reactWithEmoji(emoji) {
|
|
4875
4672
|
if (!this.ws || !this.connected) {
|
|
4876
|
-
this.logger.warn(
|
|
4877
|
-
"[ChatClient] Not connected or WebSocket missing; ignoring reactWithEmoji."
|
|
4878
|
-
);
|
|
4673
|
+
this.logger.warn("[ChatClient] Not connected or WebSocket missing; ignoring reactWithEmoji.");
|
|
4879
4674
|
return;
|
|
4880
4675
|
}
|
|
4881
4676
|
const payload = JSON.stringify({
|
|
4882
4677
|
body: JSON.stringify({ body: emoji, type: 2, v: 2 }),
|
|
4883
4678
|
kind: 1,
|
|
4884
4679
|
/*
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4680
|
+
// The 'sender' field is not required, it's not even verified by the server
|
|
4681
|
+
// Instead of passing attributes down here it's easier to ignore it
|
|
4682
|
+
sender: {
|
|
4683
|
+
user_id: null,
|
|
4684
|
+
twitter_id: null,
|
|
4685
|
+
username: null,
|
|
4686
|
+
display_name: null,
|
|
4687
|
+
},
|
|
4688
|
+
*/
|
|
4894
4689
|
payload: JSON.stringify({
|
|
4895
4690
|
room: this.spaceId,
|
|
4896
4691
|
body: JSON.stringify({ body: emoji, type: 2, v: 2 })
|
|
@@ -4994,9 +4789,6 @@ import wrtc from "@roamhq/wrtc";
|
|
|
4994
4789
|
var { nonstandard } = wrtc;
|
|
4995
4790
|
var { RTCAudioSource, RTCAudioSink } = nonstandard;
|
|
4996
4791
|
var JanusAudioSource = class extends EventEmitter2 {
|
|
4997
|
-
source;
|
|
4998
|
-
track;
|
|
4999
|
-
logger;
|
|
5000
4792
|
constructor(options) {
|
|
5001
4793
|
super();
|
|
5002
4794
|
this.logger = options?.logger;
|
|
@@ -5031,11 +4823,9 @@ var JanusAudioSource = class extends EventEmitter2 {
|
|
|
5031
4823
|
}
|
|
5032
4824
|
};
|
|
5033
4825
|
var JanusAudioSink = class extends EventEmitter2 {
|
|
5034
|
-
sink;
|
|
5035
|
-
active = true;
|
|
5036
|
-
logger;
|
|
5037
4826
|
constructor(track, options) {
|
|
5038
4827
|
super();
|
|
4828
|
+
this.active = true;
|
|
5039
4829
|
this.logger = options?.logger;
|
|
5040
4830
|
if (track.kind !== "audio") {
|
|
5041
4831
|
throw new Error("[JanusAudioSink] Provided track is not an audio track");
|
|
@@ -5069,19 +4859,13 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5069
4859
|
constructor(config) {
|
|
5070
4860
|
super();
|
|
5071
4861
|
this.config = config;
|
|
4862
|
+
this.pollActive = false;
|
|
4863
|
+
// Tracks promises waiting for specific Janus events
|
|
4864
|
+
this.eventWaiters = [];
|
|
4865
|
+
// Tracks subscriber handle+pc for each userId we subscribe to
|
|
4866
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
|
5072
4867
|
this.logger = config.logger;
|
|
5073
4868
|
}
|
|
5074
|
-
logger;
|
|
5075
|
-
sessionId;
|
|
5076
|
-
handleId;
|
|
5077
|
-
publisherId;
|
|
5078
|
-
pc;
|
|
5079
|
-
localAudioSource;
|
|
5080
|
-
pollActive = false;
|
|
5081
|
-
// Tracks promises waiting for specific Janus events
|
|
5082
|
-
eventWaiters = [];
|
|
5083
|
-
// Tracks subscriber handle+pc for each userId we subscribe to
|
|
5084
|
-
subscribers = /* @__PURE__ */ new Map();
|
|
5085
4869
|
/**
|
|
5086
4870
|
* Initializes this JanusClient for the host scenario:
|
|
5087
4871
|
* 1) createSession()
|
|
@@ -5142,10 +4926,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5142
4926
|
const evt = await evtPromise;
|
|
5143
4927
|
const data = evt.plugindata?.data;
|
|
5144
4928
|
this.publisherId = data.id;
|
|
5145
|
-
this.logger.debug(
|
|
5146
|
-
"[JanusClient] guest joined => publisherId=",
|
|
5147
|
-
this.publisherId
|
|
5148
|
-
);
|
|
4929
|
+
this.logger.debug("[JanusClient] guest joined => publisherId=", this.publisherId);
|
|
5149
4930
|
const publishers = data.publishers || [];
|
|
5150
4931
|
this.logger.debug("[JanusClient] existing publishers =>", publishers);
|
|
5151
4932
|
this.pc = new RTCPeerConnection({
|
|
@@ -5160,9 +4941,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5160
4941
|
this.setupPeerEvents();
|
|
5161
4942
|
this.enableLocalAudio();
|
|
5162
4943
|
await this.configurePublisher(sessionUUID);
|
|
5163
|
-
await Promise.all(
|
|
5164
|
-
publishers.map((pub) => this.subscribeSpeaker(pub.display, pub.id))
|
|
5165
|
-
);
|
|
4944
|
+
await Promise.all(publishers.map((pub) => this.subscribeSpeaker(pub.display, pub.id)));
|
|
5166
4945
|
this.logger.info("[JanusClient] Guest speaker negotiation complete");
|
|
5167
4946
|
}
|
|
5168
4947
|
/**
|
|
@@ -5180,9 +4959,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5180
4959
|
'discover feed_id from "publishers"'
|
|
5181
4960
|
);
|
|
5182
4961
|
const list = publishersEvt.plugindata.data.publishers;
|
|
5183
|
-
const pub = list.find(
|
|
5184
|
-
(p) => p.display === userId || p.periscope_user_id === userId
|
|
5185
|
-
);
|
|
4962
|
+
const pub = list.find((p) => p.display === userId || p.periscope_user_id === userId);
|
|
5186
4963
|
if (!pub) {
|
|
5187
4964
|
throw new Error(
|
|
5188
4965
|
`[JanusClient] subscribeSpeaker => No publisher found for userId=${userId}`
|
|
@@ -5238,9 +5015,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5238
5015
|
const val = Math.abs(frame.samples[i]);
|
|
5239
5016
|
if (val > maxVal) maxVal = val;
|
|
5240
5017
|
}
|
|
5241
|
-
this.logger.debug(
|
|
5242
|
-
`[AudioSink] userId=${userId}, maxAmplitude=${maxVal}`
|
|
5243
|
-
);
|
|
5018
|
+
this.logger.debug(`[AudioSink] userId=${userId}, maxAmplitude=${maxVal}`);
|
|
5244
5019
|
}
|
|
5245
5020
|
this.emit("audioDataFromSpeaker", {
|
|
5246
5021
|
userId,
|
|
@@ -5282,9 +5057,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5282
5057
|
*/
|
|
5283
5058
|
enableLocalAudio() {
|
|
5284
5059
|
if (!this.pc) {
|
|
5285
|
-
this.logger.warn(
|
|
5286
|
-
"[JanusClient] enableLocalAudio => No RTCPeerConnection"
|
|
5287
|
-
);
|
|
5060
|
+
this.logger.warn("[JanusClient] enableLocalAudio => No RTCPeerConnection");
|
|
5288
5061
|
return;
|
|
5289
5062
|
}
|
|
5290
5063
|
if (this.localAudioSource) {
|
|
@@ -5402,42 +5175,31 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5402
5175
|
h264_profile: "42e01f",
|
|
5403
5176
|
dummy_publisher: false
|
|
5404
5177
|
};
|
|
5405
|
-
const resp = await fetch(
|
|
5406
|
-
|
|
5407
|
-
{
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
})
|
|
5419
|
-
}
|
|
5420
|
-
);
|
|
5178
|
+
const resp = await fetch(`${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`, {
|
|
5179
|
+
method: "POST",
|
|
5180
|
+
headers: {
|
|
5181
|
+
Authorization: this.config.credential,
|
|
5182
|
+
"Content-Type": "application/json",
|
|
5183
|
+
Referer: "https://x.com"
|
|
5184
|
+
},
|
|
5185
|
+
body: JSON.stringify({
|
|
5186
|
+
janus: "message",
|
|
5187
|
+
transaction,
|
|
5188
|
+
body
|
|
5189
|
+
})
|
|
5190
|
+
});
|
|
5421
5191
|
if (!resp.ok) {
|
|
5422
5192
|
throw new Error(`[JanusClient] createRoom failed => ${resp.status}`);
|
|
5423
5193
|
}
|
|
5424
5194
|
const json = await resp.json();
|
|
5425
5195
|
this.logger.debug("[JanusClient] createRoom =>", JSON.stringify(json));
|
|
5426
5196
|
if (json.janus === "error") {
|
|
5427
|
-
throw new Error(
|
|
5428
|
-
`[JanusClient] createRoom error => ${json.error?.reason || "Unknown"}`
|
|
5429
|
-
);
|
|
5197
|
+
throw new Error(`[JanusClient] createRoom error => ${json.error?.reason || "Unknown"}`);
|
|
5430
5198
|
}
|
|
5431
5199
|
if (json.plugindata?.data?.videoroom !== "created") {
|
|
5432
|
-
throw new Error(
|
|
5433
|
-
`[JanusClient] unexpected createRoom response => ${JSON.stringify(
|
|
5434
|
-
json
|
|
5435
|
-
)}`
|
|
5436
|
-
);
|
|
5200
|
+
throw new Error(`[JanusClient] unexpected createRoom response => ${JSON.stringify(json)}`);
|
|
5437
5201
|
}
|
|
5438
|
-
this.logger.debug(
|
|
5439
|
-
`[JanusClient] Room '${this.config.roomId}' created successfully`
|
|
5440
|
-
);
|
|
5202
|
+
this.logger.debug(`[JanusClient] Room '${this.config.roomId}' created successfully`);
|
|
5441
5203
|
}
|
|
5442
5204
|
/**
|
|
5443
5205
|
* Joins the created room as a publisher, for the host scenario.
|
|
@@ -5502,26 +5264,21 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5502
5264
|
throw new Error("[JanusClient] No session for sendJanusMessage");
|
|
5503
5265
|
}
|
|
5504
5266
|
const transaction = this.randomTid();
|
|
5505
|
-
const resp = await fetch(
|
|
5506
|
-
|
|
5507
|
-
{
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
})
|
|
5519
|
-
}
|
|
5520
|
-
);
|
|
5267
|
+
const resp = await fetch(`${this.config.webrtcUrl}/${this.sessionId}/${handleId}`, {
|
|
5268
|
+
method: "POST",
|
|
5269
|
+
headers: {
|
|
5270
|
+
Authorization: this.config.credential,
|
|
5271
|
+
"Content-Type": "application/json"
|
|
5272
|
+
},
|
|
5273
|
+
body: JSON.stringify({
|
|
5274
|
+
janus: "message",
|
|
5275
|
+
transaction,
|
|
5276
|
+
body,
|
|
5277
|
+
jsep
|
|
5278
|
+
})
|
|
5279
|
+
});
|
|
5521
5280
|
if (!resp.ok) {
|
|
5522
|
-
throw new Error(
|
|
5523
|
-
`[JanusClient] sendJanusMessage failed => status=${resp.status}`
|
|
5524
|
-
);
|
|
5281
|
+
throw new Error(`[JanusClient] sendJanusMessage failed => status=${resp.status}`);
|
|
5525
5282
|
}
|
|
5526
5283
|
}
|
|
5527
5284
|
/**
|
|
@@ -5603,10 +5360,7 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5603
5360
|
return;
|
|
5604
5361
|
}
|
|
5605
5362
|
this.pc.addEventListener("iceconnectionstatechange", () => {
|
|
5606
|
-
this.logger.debug(
|
|
5607
|
-
"[JanusClient] ICE state =>",
|
|
5608
|
-
this.pc?.iceConnectionState
|
|
5609
|
-
);
|
|
5363
|
+
this.logger.debug("[JanusClient] ICE state =>", this.pc?.iceConnectionState);
|
|
5610
5364
|
if (this.pc?.iceConnectionState === "failed") {
|
|
5611
5365
|
this.emit("error", new Error("[JanusClient] ICE connection failed"));
|
|
5612
5366
|
}
|
|
@@ -5664,22 +5418,19 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5664
5418
|
periscope_user_id: this.config.userId
|
|
5665
5419
|
};
|
|
5666
5420
|
this.logger.info("[JanusClient] destroying room =>", body);
|
|
5667
|
-
const resp = await fetch(
|
|
5668
|
-
|
|
5669
|
-
{
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
})
|
|
5681
|
-
}
|
|
5682
|
-
);
|
|
5421
|
+
const resp = await fetch(`${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`, {
|
|
5422
|
+
method: "POST",
|
|
5423
|
+
headers: {
|
|
5424
|
+
Authorization: this.config.credential,
|
|
5425
|
+
"Content-Type": "application/json",
|
|
5426
|
+
Referer: "https://x.com"
|
|
5427
|
+
},
|
|
5428
|
+
body: JSON.stringify({
|
|
5429
|
+
janus: "message",
|
|
5430
|
+
transaction,
|
|
5431
|
+
body
|
|
5432
|
+
})
|
|
5433
|
+
});
|
|
5683
5434
|
if (!resp.ok) {
|
|
5684
5435
|
throw new Error(`[JanusClient] destroyRoom failed => ${resp.status}`);
|
|
5685
5436
|
}
|
|
@@ -5701,21 +5452,18 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5701
5452
|
periscope_user_id: this.config.userId
|
|
5702
5453
|
};
|
|
5703
5454
|
this.logger.info("[JanusClient] leaving room =>", body);
|
|
5704
|
-
const resp = await fetch(
|
|
5705
|
-
|
|
5706
|
-
{
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
})
|
|
5717
|
-
}
|
|
5718
|
-
);
|
|
5455
|
+
const resp = await fetch(`${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`, {
|
|
5456
|
+
method: "POST",
|
|
5457
|
+
headers: {
|
|
5458
|
+
Authorization: this.config.credential,
|
|
5459
|
+
"Content-Type": "application/json"
|
|
5460
|
+
},
|
|
5461
|
+
body: JSON.stringify({
|
|
5462
|
+
janus: "message",
|
|
5463
|
+
transaction,
|
|
5464
|
+
body
|
|
5465
|
+
})
|
|
5466
|
+
});
|
|
5719
5467
|
if (!resp.ok) {
|
|
5720
5468
|
throw new Error(`[JanusClient] leaveRoom => error code ${resp.status}`);
|
|
5721
5469
|
}
|
|
@@ -5729,18 +5477,12 @@ var Space = class extends EventEmitter4 {
|
|
|
5729
5477
|
constructor(client, options) {
|
|
5730
5478
|
super();
|
|
5731
5479
|
this.client = client;
|
|
5480
|
+
this.isInitialized = false;
|
|
5481
|
+
this.plugins = /* @__PURE__ */ new Set();
|
|
5482
|
+
this.speakers = /* @__PURE__ */ new Map();
|
|
5732
5483
|
this.debug = options?.debug ?? false;
|
|
5733
5484
|
this.logger = new Logger(this.debug);
|
|
5734
5485
|
}
|
|
5735
|
-
debug;
|
|
5736
|
-
logger;
|
|
5737
|
-
janusClient;
|
|
5738
|
-
chatClient;
|
|
5739
|
-
authToken;
|
|
5740
|
-
broadcastInfo;
|
|
5741
|
-
isInitialized = false;
|
|
5742
|
-
plugins = /* @__PURE__ */ new Set();
|
|
5743
|
-
speakers = /* @__PURE__ */ new Map();
|
|
5744
5486
|
/**
|
|
5745
5487
|
* Registers a plugin and calls its onAttach(...).
|
|
5746
5488
|
* init(...) will be invoked once initialization is complete.
|
|
@@ -5796,16 +5538,11 @@ var Space = class extends EventEmitter4 {
|
|
|
5796
5538
|
this.janusClient.on("subscribedSpeaker", ({ userId, feedId }) => {
|
|
5797
5539
|
const speaker = this.speakers.get(userId);
|
|
5798
5540
|
if (!speaker) {
|
|
5799
|
-
this.logger.debug(
|
|
5800
|
-
"[Space] subscribedSpeaker => no speaker found",
|
|
5801
|
-
userId
|
|
5802
|
-
);
|
|
5541
|
+
this.logger.debug("[Space] subscribedSpeaker => no speaker found", userId);
|
|
5803
5542
|
return;
|
|
5804
5543
|
}
|
|
5805
5544
|
speaker.janusParticipantId = feedId;
|
|
5806
|
-
this.logger.debug(
|
|
5807
|
-
`[Space] updated speaker => userId=${userId}, feedId=${feedId}`
|
|
5808
|
-
);
|
|
5545
|
+
this.logger.debug(`[Space] updated speaker => userId=${userId}, feedId=${feedId}`);
|
|
5809
5546
|
});
|
|
5810
5547
|
this.logger.debug("[Space] Publishing broadcast...");
|
|
5811
5548
|
await publishBroadcast({
|
|
@@ -5827,10 +5564,7 @@ var Space = class extends EventEmitter4 {
|
|
|
5827
5564
|
await this.chatClient.connect();
|
|
5828
5565
|
this.setupChatEvents();
|
|
5829
5566
|
}
|
|
5830
|
-
this.logger.info(
|
|
5831
|
-
"[Space] Initialized =>",
|
|
5832
|
-
broadcast.share_url.replace("broadcasts", "spaces")
|
|
5833
|
-
);
|
|
5567
|
+
this.logger.info("[Space] Initialized =>", broadcast.share_url.replace("broadcasts", "spaces"));
|
|
5834
5568
|
this.isInitialized = true;
|
|
5835
5569
|
for (const { plugin, config: pluginConfig } of this.plugins) {
|
|
5836
5570
|
plugin.init?.({ space: this, pluginConfig });
|
|
@@ -5864,12 +5598,7 @@ var Space = class extends EventEmitter4 {
|
|
|
5864
5598
|
throw new Error("[Space] No auth token available");
|
|
5865
5599
|
}
|
|
5866
5600
|
this.speakers.set(userId, { userId, sessionUUID });
|
|
5867
|
-
await this.callApproveEndpoint(
|
|
5868
|
-
this.broadcastInfo,
|
|
5869
|
-
this.authToken,
|
|
5870
|
-
userId,
|
|
5871
|
-
sessionUUID
|
|
5872
|
-
);
|
|
5601
|
+
await this.callApproveEndpoint(this.broadcastInfo, this.authToken, userId, sessionUUID);
|
|
5873
5602
|
await this.janusClient?.subscribeSpeaker(userId);
|
|
5874
5603
|
}
|
|
5875
5604
|
/**
|
|
@@ -5896,9 +5625,7 @@ var Space = class extends EventEmitter4 {
|
|
|
5896
5625
|
});
|
|
5897
5626
|
if (!resp.ok) {
|
|
5898
5627
|
const error = await resp.text();
|
|
5899
|
-
throw new Error(
|
|
5900
|
-
`[Space] Failed to approve speaker => ${resp.status}: ${error}`
|
|
5901
|
-
);
|
|
5628
|
+
throw new Error(`[Space] Failed to approve speaker => ${resp.status}: ${error}`);
|
|
5902
5629
|
}
|
|
5903
5630
|
this.logger.info("[Space] Speaker approved =>", userId);
|
|
5904
5631
|
}
|
|
@@ -5917,17 +5644,10 @@ var Space = class extends EventEmitter4 {
|
|
|
5917
5644
|
}
|
|
5918
5645
|
const speaker = this.speakers.get(userId);
|
|
5919
5646
|
if (!speaker) {
|
|
5920
|
-
throw new Error(
|
|
5921
|
-
`[Space] removeSpeaker => no speaker found for userId=${userId}`
|
|
5922
|
-
);
|
|
5647
|
+
throw new Error(`[Space] removeSpeaker => no speaker found for userId=${userId}`);
|
|
5923
5648
|
}
|
|
5924
5649
|
const { sessionUUID, janusParticipantId } = speaker;
|
|
5925
|
-
this.logger.debug(
|
|
5926
|
-
"[Space] removeSpeaker =>",
|
|
5927
|
-
sessionUUID,
|
|
5928
|
-
janusParticipantId,
|
|
5929
|
-
speaker
|
|
5930
|
-
);
|
|
5650
|
+
this.logger.debug("[Space] removeSpeaker =>", sessionUUID, janusParticipantId, speaker);
|
|
5931
5651
|
if (!sessionUUID || janusParticipantId === void 0) {
|
|
5932
5652
|
throw new Error(
|
|
5933
5653
|
`[Space] removeSpeaker => missing sessionUUID or feedId for userId=${userId}`
|
|
@@ -5936,9 +5656,7 @@ var Space = class extends EventEmitter4 {
|
|
|
5936
5656
|
const janusHandleId = this.janusClient.getHandleId();
|
|
5937
5657
|
const janusSessionId = this.janusClient.getSessionId();
|
|
5938
5658
|
if (!janusHandleId || !janusSessionId) {
|
|
5939
|
-
throw new Error(
|
|
5940
|
-
`[Space] removeSpeaker => missing Janus handle/session for userId=${userId}`
|
|
5941
|
-
);
|
|
5659
|
+
throw new Error(`[Space] removeSpeaker => missing Janus handle/session for userId=${userId}`);
|
|
5942
5660
|
}
|
|
5943
5661
|
await this.callRemoveEndpoint(
|
|
5944
5662
|
this.broadcastInfo,
|
|
@@ -5980,9 +5698,7 @@ var Space = class extends EventEmitter4 {
|
|
|
5980
5698
|
});
|
|
5981
5699
|
if (!resp.ok) {
|
|
5982
5700
|
const error = await resp.text();
|
|
5983
|
-
throw new Error(
|
|
5984
|
-
`[Space] Failed to remove speaker => ${resp.status}: ${error}`
|
|
5985
|
-
);
|
|
5701
|
+
throw new Error(`[Space] Failed to remove speaker => ${resp.status}: ${error}`);
|
|
5986
5702
|
}
|
|
5987
5703
|
this.logger.debug("[Space] Speaker removed => sessionUUID=", sessionUUID);
|
|
5988
5704
|
}
|
|
@@ -6178,32 +5894,12 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6178
5894
|
constructor(client, config) {
|
|
6179
5895
|
super();
|
|
6180
5896
|
this.client = client;
|
|
5897
|
+
// Plugin management
|
|
5898
|
+
this.plugins = /* @__PURE__ */ new Set();
|
|
6181
5899
|
this.spaceId = config.spaceId;
|
|
6182
5900
|
this.debug = config.debug ?? false;
|
|
6183
5901
|
this.logger = new Logger(this.debug);
|
|
6184
5902
|
}
|
|
6185
|
-
spaceId;
|
|
6186
|
-
debug;
|
|
6187
|
-
logger;
|
|
6188
|
-
// Basic auth/cookie data
|
|
6189
|
-
cookie;
|
|
6190
|
-
authToken;
|
|
6191
|
-
// Chat
|
|
6192
|
-
chatJwtToken;
|
|
6193
|
-
chatToken;
|
|
6194
|
-
chatClient;
|
|
6195
|
-
// Watch session
|
|
6196
|
-
lifecycleToken;
|
|
6197
|
-
watchSession;
|
|
6198
|
-
// HLS stream
|
|
6199
|
-
hlsUrl;
|
|
6200
|
-
// Speaker request + Janus
|
|
6201
|
-
sessionUUID;
|
|
6202
|
-
janusJwt;
|
|
6203
|
-
webrtcGwUrl;
|
|
6204
|
-
janusClient;
|
|
6205
|
-
// Plugin management
|
|
6206
|
-
plugins = /* @__PURE__ */ new Set();
|
|
6207
5903
|
/**
|
|
6208
5904
|
* Adds a plugin and calls its onAttach immediately.
|
|
6209
5905
|
* init() or onJanusReady() will be invoked later at the appropriate time.
|
|
@@ -6211,10 +5907,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6211
5907
|
use(plugin, config) {
|
|
6212
5908
|
const registration = { plugin, config };
|
|
6213
5909
|
this.plugins.add(registration);
|
|
6214
|
-
this.logger.debug(
|
|
6215
|
-
"[SpaceParticipant] Plugin added =>",
|
|
6216
|
-
plugin.constructor.name
|
|
6217
|
-
);
|
|
5910
|
+
this.logger.debug("[SpaceParticipant] Plugin added =>", plugin.constructor.name);
|
|
6218
5911
|
plugin.onAttach?.({ space: this, pluginConfig: config });
|
|
6219
5912
|
if (plugin.init) {
|
|
6220
5913
|
plugin.init({ space: this, pluginConfig: config });
|
|
@@ -6225,10 +5918,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6225
5918
|
* Joins the Space as a listener: obtains HLS, chat token, etc.
|
|
6226
5919
|
*/
|
|
6227
5920
|
async joinAsListener() {
|
|
6228
|
-
this.logger.info(
|
|
6229
|
-
"[SpaceParticipant] Joining space as listener =>",
|
|
6230
|
-
this.spaceId
|
|
6231
|
-
);
|
|
5921
|
+
this.logger.info("[SpaceParticipant] Joining space as listener =>", this.spaceId);
|
|
6232
5922
|
this.cookie = await this.client.getPeriscopeCookie();
|
|
6233
5923
|
this.authToken = await authorizeToken(this.cookie);
|
|
6234
5924
|
const spaceMeta = await this.client.getAudioSpaceById(this.spaceId);
|
|
@@ -6270,9 +5960,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6270
5960
|
*/
|
|
6271
5961
|
async requestSpeaker() {
|
|
6272
5962
|
if (!this.chatJwtToken) {
|
|
6273
|
-
throw new Error(
|
|
6274
|
-
"[SpaceParticipant] Must join as listener first (no chat token)."
|
|
6275
|
-
);
|
|
5963
|
+
throw new Error("[SpaceParticipant] Must join as listener first (no chat token).");
|
|
6276
5964
|
}
|
|
6277
5965
|
if (!this.authToken) {
|
|
6278
5966
|
throw new Error("[SpaceParticipant] No auth token available.");
|
|
@@ -6287,10 +5975,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6287
5975
|
authToken: this.authToken
|
|
6288
5976
|
});
|
|
6289
5977
|
this.sessionUUID = session_uuid;
|
|
6290
|
-
this.logger.info(
|
|
6291
|
-
"[SpaceParticipant] Speaker request submitted =>",
|
|
6292
|
-
session_uuid
|
|
6293
|
-
);
|
|
5978
|
+
this.logger.info("[SpaceParticipant] Speaker request submitted =>", session_uuid);
|
|
6294
5979
|
return { sessionUUID: session_uuid };
|
|
6295
5980
|
}
|
|
6296
5981
|
/**
|
|
@@ -6315,10 +6000,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6315
6000
|
chatToken: this.chatToken,
|
|
6316
6001
|
authToken: this.authToken
|
|
6317
6002
|
});
|
|
6318
|
-
this.logger.info(
|
|
6319
|
-
"[SpaceParticipant] Speaker request canceled =>",
|
|
6320
|
-
this.sessionUUID
|
|
6321
|
-
);
|
|
6003
|
+
this.logger.info("[SpaceParticipant] Speaker request canceled =>", this.sessionUUID);
|
|
6322
6004
|
this.sessionUUID = void 0;
|
|
6323
6005
|
}
|
|
6324
6006
|
/**
|
|
@@ -6327,13 +6009,9 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6327
6009
|
*/
|
|
6328
6010
|
async becomeSpeaker() {
|
|
6329
6011
|
if (!this.sessionUUID) {
|
|
6330
|
-
throw new Error(
|
|
6331
|
-
"[SpaceParticipant] No sessionUUID (did you call requestSpeaker()?)."
|
|
6332
|
-
);
|
|
6012
|
+
throw new Error("[SpaceParticipant] No sessionUUID (did you call requestSpeaker()?).");
|
|
6333
6013
|
}
|
|
6334
|
-
this.logger.info(
|
|
6335
|
-
"[SpaceParticipant] Negotiating speaker role via Janus..."
|
|
6336
|
-
);
|
|
6014
|
+
this.logger.info("[SpaceParticipant] Negotiating speaker role via Janus...");
|
|
6337
6015
|
const turnServers = await getTurnServers(this.cookie);
|
|
6338
6016
|
this.logger.debug("[SpaceParticipant] turnServers =>", turnServers);
|
|
6339
6017
|
const nego = await negotiateGuestStream({
|
|
@@ -6356,16 +6034,10 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6356
6034
|
});
|
|
6357
6035
|
await this.janusClient.initializeGuestSpeaker(this.sessionUUID);
|
|
6358
6036
|
this.janusClient.on("audioDataFromSpeaker", (data) => {
|
|
6359
|
-
this.logger.debug(
|
|
6360
|
-
"[SpaceParticipant] Received speaker audio =>",
|
|
6361
|
-
data.userId
|
|
6362
|
-
);
|
|
6037
|
+
this.logger.debug("[SpaceParticipant] Received speaker audio =>", data.userId);
|
|
6363
6038
|
this.handleAudioData(data);
|
|
6364
6039
|
});
|
|
6365
|
-
this.logger.info(
|
|
6366
|
-
"[SpaceParticipant] Now speaker on the Space =>",
|
|
6367
|
-
this.spaceId
|
|
6368
|
-
);
|
|
6040
|
+
this.logger.info("[SpaceParticipant] Now speaker on the Space =>", this.spaceId);
|
|
6369
6041
|
for (const { plugin } of this.plugins) {
|
|
6370
6042
|
plugin.onJanusReady?.(this.janusClient);
|
|
6371
6043
|
}
|
|
@@ -6375,9 +6047,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6375
6047
|
*/
|
|
6376
6048
|
async removeFromSpeaker() {
|
|
6377
6049
|
if (!this.sessionUUID) {
|
|
6378
|
-
throw new Error(
|
|
6379
|
-
"[SpaceParticipant] No sessionUUID; cannot remove from speaker role."
|
|
6380
|
-
);
|
|
6050
|
+
throw new Error("[SpaceParticipant] No sessionUUID; cannot remove from speaker role.");
|
|
6381
6051
|
}
|
|
6382
6052
|
if (!this.authToken || !this.chatToken) {
|
|
6383
6053
|
throw new Error("[SpaceParticipant] Missing authToken or chatToken.");
|
|
@@ -6387,9 +6057,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6387
6057
|
await this.janusClient.stop();
|
|
6388
6058
|
this.janusClient = void 0;
|
|
6389
6059
|
}
|
|
6390
|
-
this.logger.info(
|
|
6391
|
-
"[SpaceParticipant] Successfully removed from speaker role."
|
|
6392
|
-
);
|
|
6060
|
+
this.logger.info("[SpaceParticipant] Successfully removed from speaker role.");
|
|
6393
6061
|
}
|
|
6394
6062
|
/**
|
|
6395
6063
|
* Leaves the Space gracefully:
|
|
@@ -6417,9 +6085,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6417
6085
|
*/
|
|
6418
6086
|
pushAudio(samples, sampleRate) {
|
|
6419
6087
|
if (!this.janusClient) {
|
|
6420
|
-
this.logger.warn(
|
|
6421
|
-
"[SpaceParticipant] Not a speaker yet; ignoring pushAudio."
|
|
6422
|
-
);
|
|
6088
|
+
this.logger.warn("[SpaceParticipant] Not a speaker yet; ignoring pushAudio.");
|
|
6423
6089
|
return;
|
|
6424
6090
|
}
|
|
6425
6091
|
this.janusClient.pushLocalAudio(samples, sampleRate);
|
|
@@ -6441,9 +6107,7 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6441
6107
|
this.chatClient.on("newSpeakerAccepted", ({ userId }) => {
|
|
6442
6108
|
this.logger.debug("[SpaceParticipant] newSpeakerAccepted =>", userId);
|
|
6443
6109
|
if (!this.janusClient) {
|
|
6444
|
-
this.logger.warn(
|
|
6445
|
-
"[SpaceParticipant] No janusClient yet; ignoring new speaker..."
|
|
6446
|
-
);
|
|
6110
|
+
this.logger.warn("[SpaceParticipant] No janusClient yet; ignoring new speaker...");
|
|
6447
6111
|
return;
|
|
6448
6112
|
}
|
|
6449
6113
|
if (userId === this.janusClient.getHandleId()) {
|
|
@@ -6492,6 +6156,9 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6492
6156
|
}
|
|
6493
6157
|
};
|
|
6494
6158
|
|
|
6159
|
+
// src/client/spaces/plugins/SttTtsPlugin.ts
|
|
6160
|
+
import { logger } from "@elizaos/core";
|
|
6161
|
+
|
|
6495
6162
|
// src/client/spaces/plugins/IdleMonitorPlugin.ts
|
|
6496
6163
|
var IdleMonitorPlugin = class {
|
|
6497
6164
|
/**
|
|
@@ -6501,12 +6168,9 @@ var IdleMonitorPlugin = class {
|
|
|
6501
6168
|
constructor(idleTimeoutMs = 6e4, checkEveryMs = 1e4) {
|
|
6502
6169
|
this.idleTimeoutMs = idleTimeoutMs;
|
|
6503
6170
|
this.checkEveryMs = checkEveryMs;
|
|
6171
|
+
this.lastSpeakerAudioMs = Date.now();
|
|
6172
|
+
this.lastLocalAudioMs = Date.now();
|
|
6504
6173
|
}
|
|
6505
|
-
space;
|
|
6506
|
-
logger;
|
|
6507
|
-
lastSpeakerAudioMs = Date.now();
|
|
6508
|
-
lastLocalAudioMs = Date.now();
|
|
6509
|
-
checkInterval;
|
|
6510
6174
|
/**
|
|
6511
6175
|
* Called immediately after .use(plugin).
|
|
6512
6176
|
* Allows for minimal setup, including obtaining a debug logger if desired.
|
|
@@ -6532,10 +6196,7 @@ var IdleMonitorPlugin = class {
|
|
|
6532
6196
|
this.lastLocalAudioMs = Date.now();
|
|
6533
6197
|
originalPushAudio(samples, sampleRate);
|
|
6534
6198
|
};
|
|
6535
|
-
this.checkInterval = setInterval(
|
|
6536
|
-
() => this.checkIdle(),
|
|
6537
|
-
this.checkEveryMs
|
|
6538
|
-
);
|
|
6199
|
+
this.checkInterval = setInterval(() => this.checkIdle(), this.checkEveryMs);
|
|
6539
6200
|
}
|
|
6540
6201
|
/**
|
|
6541
6202
|
* Checks if we've exceeded idleTimeoutMs with no audio activity.
|
|
@@ -6546,9 +6207,7 @@ var IdleMonitorPlugin = class {
|
|
|
6546
6207
|
const lastAudio = Math.max(this.lastSpeakerAudioMs, this.lastLocalAudioMs);
|
|
6547
6208
|
const idleMs = now - lastAudio;
|
|
6548
6209
|
if (idleMs >= this.idleTimeoutMs) {
|
|
6549
|
-
this.logger?.warn(
|
|
6550
|
-
`[IdleMonitorPlugin] idleTimeout => no audio for ${idleMs}ms`
|
|
6551
|
-
);
|
|
6210
|
+
this.logger?.warn(`[IdleMonitorPlugin] idleTimeout => no audio for ${idleMs}ms`);
|
|
6552
6211
|
this.space?.emit("idleTimeout", { idleMs });
|
|
6553
6212
|
}
|
|
6554
6213
|
}
|
|
@@ -6576,38 +6235,34 @@ var IdleMonitorPlugin = class {
|
|
|
6576
6235
|
import { spawn } from "node:child_process";
|
|
6577
6236
|
import {
|
|
6578
6237
|
ChannelType,
|
|
6579
|
-
|
|
6238
|
+
EventType,
|
|
6239
|
+
ModelType,
|
|
6580
6240
|
createUniqueUuid,
|
|
6581
|
-
logger
|
|
6241
|
+
logger as logger2
|
|
6582
6242
|
} from "@elizaos/core";
|
|
6583
6243
|
var VOLUME_WINDOW_SIZE = 100;
|
|
6584
6244
|
var SPEAKING_THRESHOLD = 0.05;
|
|
6585
6245
|
var SILENCE_DETECTION_THRESHOLD_MS = 1e3;
|
|
6586
6246
|
var SttTtsPlugin2 = class {
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
userSpeakingTimer = null;
|
|
6602
|
-
volumeBuffers;
|
|
6603
|
-
ttsAbortController = null;
|
|
6247
|
+
constructor() {
|
|
6248
|
+
this.name = "SttTtsPlugin";
|
|
6249
|
+
this.description = "Speech-to-text (OpenAI) + conversation + TTS (ElevenLabs)";
|
|
6250
|
+
/**
|
|
6251
|
+
* userId => arrayOfChunks (PCM Int16)
|
|
6252
|
+
*/
|
|
6253
|
+
this.pcmBuffers = /* @__PURE__ */ new Map();
|
|
6254
|
+
// TTS queue for sequentially speaking
|
|
6255
|
+
this.ttsQueue = [];
|
|
6256
|
+
this.isSpeaking = false;
|
|
6257
|
+
this.isProcessingAudio = false;
|
|
6258
|
+
this.userSpeakingTimer = null;
|
|
6259
|
+
this.ttsAbortController = null;
|
|
6260
|
+
}
|
|
6604
6261
|
onAttach(_space) {
|
|
6605
|
-
|
|
6262
|
+
logger2.log("[SttTtsPlugin] onAttach => space was attached");
|
|
6606
6263
|
}
|
|
6607
6264
|
async init(params) {
|
|
6608
|
-
|
|
6609
|
-
"[SttTtsPlugin] init => Space fully ready. Subscribing to events."
|
|
6610
|
-
);
|
|
6265
|
+
logger2.log("[SttTtsPlugin] init => Space fully ready. Subscribing to events.");
|
|
6611
6266
|
this.space = params.space;
|
|
6612
6267
|
this.janus = this.space?.janusClient;
|
|
6613
6268
|
const config = params.pluginConfig;
|
|
@@ -6642,13 +6297,10 @@ var SttTtsPlugin2 = class {
|
|
|
6642
6297
|
arr.push(data.samples);
|
|
6643
6298
|
if (!this.isSpeaking) {
|
|
6644
6299
|
this.userSpeakingTimer = setTimeout(() => {
|
|
6645
|
-
|
|
6646
|
-
"[SttTtsPlugin] start processing audio for user =>",
|
|
6647
|
-
data.userId
|
|
6648
|
-
);
|
|
6300
|
+
logger2.log("[SttTtsPlugin] start processing audio for user =>", data.userId);
|
|
6649
6301
|
this.userSpeakingTimer = null;
|
|
6650
6302
|
this.processAudio(data.userId).catch(
|
|
6651
|
-
(err) =>
|
|
6303
|
+
(err) => logger2.error("[SttTtsPlugin] handleSilence error =>", err)
|
|
6652
6304
|
);
|
|
6653
6305
|
}, SILENCE_DETECTION_THRESHOLD_MS);
|
|
6654
6306
|
} else {
|
|
@@ -6673,7 +6325,7 @@ var SttTtsPlugin2 = class {
|
|
|
6673
6325
|
if (this.ttsAbortController) {
|
|
6674
6326
|
this.ttsAbortController.abort();
|
|
6675
6327
|
this.isSpeaking = false;
|
|
6676
|
-
|
|
6328
|
+
logger2.log("[SttTtsPlugin] TTS playback interrupted");
|
|
6677
6329
|
}
|
|
6678
6330
|
}
|
|
6679
6331
|
}
|
|
@@ -6719,16 +6371,14 @@ var SttTtsPlugin2 = class {
|
|
|
6719
6371
|
}
|
|
6720
6372
|
this.isProcessingAudio = true;
|
|
6721
6373
|
try {
|
|
6722
|
-
|
|
6374
|
+
logger2.log("[SttTtsPlugin] Starting audio processing for user:", userId);
|
|
6723
6375
|
const chunks = this.pcmBuffers.get(userId) || [];
|
|
6724
6376
|
this.pcmBuffers.clear();
|
|
6725
6377
|
if (!chunks.length) {
|
|
6726
|
-
|
|
6378
|
+
logger2.warn("[SttTtsPlugin] No audio chunks for user =>", userId);
|
|
6727
6379
|
return;
|
|
6728
6380
|
}
|
|
6729
|
-
|
|
6730
|
-
`[SttTtsPlugin] Flushing STT buffer for user=${userId}, chunks=${chunks.length}`
|
|
6731
|
-
);
|
|
6381
|
+
logger2.log(`[SttTtsPlugin] Flushing STT buffer for user=${userId}, chunks=${chunks.length}`);
|
|
6732
6382
|
const totalLen = chunks.reduce((acc, c) => acc + c.length, 0);
|
|
6733
6383
|
const merged = new Int16Array(totalLen);
|
|
6734
6384
|
let offset = 0;
|
|
@@ -6737,19 +6387,16 @@ var SttTtsPlugin2 = class {
|
|
|
6737
6387
|
offset += c.length;
|
|
6738
6388
|
}
|
|
6739
6389
|
const wavBuffer = await this.convertPcmToWavInMemory(merged, 48e3);
|
|
6740
|
-
const sttText = await this.runtime.useModel(
|
|
6741
|
-
|
|
6742
|
-
wavBuffer
|
|
6743
|
-
);
|
|
6744
|
-
logger.log(`[SttTtsPlugin] Transcription result: "${sttText}"`);
|
|
6390
|
+
const sttText = await this.runtime.useModel(ModelType.TRANSCRIPTION, wavBuffer);
|
|
6391
|
+
logger2.log(`[SttTtsPlugin] Transcription result: "${sttText}"`);
|
|
6745
6392
|
if (!sttText || !sttText.trim()) {
|
|
6746
|
-
|
|
6393
|
+
logger2.warn("[SttTtsPlugin] No speech recognized for user =>", userId);
|
|
6747
6394
|
return;
|
|
6748
6395
|
}
|
|
6749
|
-
|
|
6396
|
+
logger2.log(`[SttTtsPlugin] STT => user=${userId}, text="${sttText}"`);
|
|
6750
6397
|
await this.handleUserMessage(sttText, userId);
|
|
6751
6398
|
} catch (error) {
|
|
6752
|
-
|
|
6399
|
+
logger2.error("[SttTtsPlugin] processAudio error =>", error);
|
|
6753
6400
|
} finally {
|
|
6754
6401
|
this.isProcessingAudio = false;
|
|
6755
6402
|
}
|
|
@@ -6762,7 +6409,7 @@ var SttTtsPlugin2 = class {
|
|
|
6762
6409
|
if (!this.isSpeaking) {
|
|
6763
6410
|
this.isSpeaking = true;
|
|
6764
6411
|
this.processTtsQueue().catch((err) => {
|
|
6765
|
-
|
|
6412
|
+
logger2.error("[SttTtsPlugin] processTtsQueue error =>", err);
|
|
6766
6413
|
});
|
|
6767
6414
|
}
|
|
6768
6415
|
}
|
|
@@ -6776,22 +6423,19 @@ var SttTtsPlugin2 = class {
|
|
|
6776
6423
|
this.ttsAbortController = new AbortController();
|
|
6777
6424
|
const { signal } = this.ttsAbortController;
|
|
6778
6425
|
try {
|
|
6779
|
-
const responseStream = await this.runtime.useModel(
|
|
6780
|
-
ModelTypes.TEXT_TO_SPEECH,
|
|
6781
|
-
text
|
|
6782
|
-
);
|
|
6426
|
+
const responseStream = await this.runtime.useModel(ModelType.TEXT_TO_SPEECH, text);
|
|
6783
6427
|
if (!responseStream) {
|
|
6784
|
-
|
|
6428
|
+
logger2.error("[SttTtsPlugin] TTS responseStream is null");
|
|
6785
6429
|
continue;
|
|
6786
6430
|
}
|
|
6787
|
-
|
|
6431
|
+
logger2.log("[SttTtsPlugin] Received ElevenLabs TTS stream");
|
|
6788
6432
|
await this.streamTtsStreamToJanus(responseStream, 48e3, signal);
|
|
6789
6433
|
if (signal.aborted) {
|
|
6790
|
-
|
|
6434
|
+
logger2.log("[SttTtsPlugin] TTS interrupted after streaming");
|
|
6791
6435
|
return;
|
|
6792
6436
|
}
|
|
6793
6437
|
} catch (err) {
|
|
6794
|
-
|
|
6438
|
+
logger2.error("[SttTtsPlugin] TTS streaming error =>", err);
|
|
6795
6439
|
} finally {
|
|
6796
6440
|
this.ttsAbortController = null;
|
|
6797
6441
|
}
|
|
@@ -6806,14 +6450,11 @@ var SttTtsPlugin2 = class {
|
|
|
6806
6450
|
return null;
|
|
6807
6451
|
}
|
|
6808
6452
|
const numericId = userId.replace("tw-", "");
|
|
6809
|
-
const roomId = createUniqueUuid(
|
|
6810
|
-
this.runtime,
|
|
6811
|
-
`twitter_generate_room-${this.spaceId}`
|
|
6812
|
-
);
|
|
6453
|
+
const roomId = createUniqueUuid(this.runtime, `twitter_generate_room-${this.spaceId}`);
|
|
6813
6454
|
const userUuid = createUniqueUuid(this.runtime, numericId);
|
|
6814
|
-
const entity = await this.runtime.
|
|
6455
|
+
const entity = await this.runtime.getEntityById(userUuid);
|
|
6815
6456
|
if (!entity) {
|
|
6816
|
-
await this.runtime.
|
|
6457
|
+
await this.runtime.createEntity({
|
|
6817
6458
|
id: userUuid,
|
|
6818
6459
|
names: [userId],
|
|
6819
6460
|
agentId: this.runtime.agentId
|
|
@@ -6829,10 +6470,7 @@ var SttTtsPlugin2 = class {
|
|
|
6829
6470
|
});
|
|
6830
6471
|
await this.runtime.ensureParticipantInRoom(userUuid, roomId);
|
|
6831
6472
|
const memory = {
|
|
6832
|
-
id: createUniqueUuid(
|
|
6833
|
-
this.runtime,
|
|
6834
|
-
`${roomId}-voice-message-${Date.now()}`
|
|
6835
|
-
),
|
|
6473
|
+
id: createUniqueUuid(this.runtime, `${roomId}-voice-message-${Date.now()}`),
|
|
6836
6474
|
agentId: this.runtime.agentId,
|
|
6837
6475
|
content: {
|
|
6838
6476
|
text: userText,
|
|
@@ -6845,10 +6483,7 @@ var SttTtsPlugin2 = class {
|
|
|
6845
6483
|
const callback = async (content, _files = []) => {
|
|
6846
6484
|
try {
|
|
6847
6485
|
const responseMemory = {
|
|
6848
|
-
id: createUniqueUuid(
|
|
6849
|
-
this.runtime,
|
|
6850
|
-
`${memory.id}-voice-response-${Date.now()}`
|
|
6851
|
-
),
|
|
6486
|
+
id: createUniqueUuid(this.runtime, `${memory.id}-voice-response-${Date.now()}`),
|
|
6852
6487
|
entityId: this.runtime.agentId,
|
|
6853
6488
|
agentId: this.runtime.agentId,
|
|
6854
6489
|
content: {
|
|
@@ -6861,7 +6496,7 @@ var SttTtsPlugin2 = class {
|
|
|
6861
6496
|
createdAt: Date.now()
|
|
6862
6497
|
};
|
|
6863
6498
|
if (responseMemory.content.text?.trim()) {
|
|
6864
|
-
await this.runtime.
|
|
6499
|
+
await this.runtime.createMemory(responseMemory);
|
|
6865
6500
|
this.isProcessingAudio = false;
|
|
6866
6501
|
this.volumeBuffers.clear();
|
|
6867
6502
|
await this.speakText(content.text);
|
|
@@ -6872,7 +6507,7 @@ var SttTtsPlugin2 = class {
|
|
|
6872
6507
|
return [];
|
|
6873
6508
|
}
|
|
6874
6509
|
};
|
|
6875
|
-
this.runtime.emitEvent(
|
|
6510
|
+
this.runtime.emitEvent(EventType.VOICE_MESSAGE_RECEIVED, {
|
|
6876
6511
|
runtime: this.runtime,
|
|
6877
6512
|
message: memory,
|
|
6878
6513
|
callback
|
|
@@ -6905,11 +6540,7 @@ var SttTtsPlugin2 = class {
|
|
|
6905
6540
|
reject(new Error(`ffmpeg error code=${code}`));
|
|
6906
6541
|
return;
|
|
6907
6542
|
}
|
|
6908
|
-
const samples = new Int16Array(
|
|
6909
|
-
raw.buffer,
|
|
6910
|
-
raw.byteOffset,
|
|
6911
|
-
raw.byteLength / 2
|
|
6912
|
-
);
|
|
6543
|
+
const samples = new Int16Array(raw.buffer, raw.byteOffset, raw.byteLength / 2);
|
|
6913
6544
|
resolve(samples);
|
|
6914
6545
|
});
|
|
6915
6546
|
ff.stdin.write(mp3Buf);
|
|
@@ -6924,7 +6555,7 @@ var SttTtsPlugin2 = class {
|
|
|
6924
6555
|
const FRAME_SIZE = Math.floor(sampleRate * 0.01);
|
|
6925
6556
|
for (let offset = 0; offset + FRAME_SIZE <= samples.length; offset += FRAME_SIZE) {
|
|
6926
6557
|
if (this.ttsAbortController?.signal.aborted) {
|
|
6927
|
-
|
|
6558
|
+
logger2.log("[SttTtsPlugin] streamToJanus interrupted");
|
|
6928
6559
|
return;
|
|
6929
6560
|
}
|
|
6930
6561
|
const frame = new Int16Array(FRAME_SIZE);
|
|
@@ -6938,7 +6569,7 @@ var SttTtsPlugin2 = class {
|
|
|
6938
6569
|
return new Promise((resolve, reject) => {
|
|
6939
6570
|
stream.on("data", (chunk) => {
|
|
6940
6571
|
if (signal.aborted) {
|
|
6941
|
-
|
|
6572
|
+
logger2.log("[SttTtsPlugin] Stream aborted, stopping playback");
|
|
6942
6573
|
stream.destroy();
|
|
6943
6574
|
reject(new Error("TTS streaming aborted"));
|
|
6944
6575
|
return;
|
|
@@ -6947,7 +6578,7 @@ var SttTtsPlugin2 = class {
|
|
|
6947
6578
|
});
|
|
6948
6579
|
stream.on("end", async () => {
|
|
6949
6580
|
if (signal.aborted) {
|
|
6950
|
-
|
|
6581
|
+
logger2.log("[SttTtsPlugin] Stream ended but was aborted");
|
|
6951
6582
|
return reject(new Error("TTS streaming aborted"));
|
|
6952
6583
|
}
|
|
6953
6584
|
const mp3Buffer = Buffer.concat(chunks);
|
|
@@ -6960,13 +6591,13 @@ var SttTtsPlugin2 = class {
|
|
|
6960
6591
|
}
|
|
6961
6592
|
});
|
|
6962
6593
|
stream.on("error", (error) => {
|
|
6963
|
-
|
|
6594
|
+
logger2.error("[SttTtsPlugin] Error in TTS stream", error);
|
|
6964
6595
|
reject(error);
|
|
6965
6596
|
});
|
|
6966
6597
|
});
|
|
6967
6598
|
}
|
|
6968
6599
|
cleanup() {
|
|
6969
|
-
|
|
6600
|
+
logger2.log("[SttTtsPlugin] cleanup => releasing resources");
|
|
6970
6601
|
this.pcmBuffers.clear();
|
|
6971
6602
|
this.userSpeakingTimer = null;
|
|
6972
6603
|
this.ttsQueue = [];
|
|
@@ -6980,111 +6611,11 @@ import fs from "node:fs";
|
|
|
6980
6611
|
import path from "node:path";
|
|
6981
6612
|
import {
|
|
6982
6613
|
ChannelType as ChannelType2,
|
|
6983
|
-
|
|
6614
|
+
ModelType as ModelType2,
|
|
6984
6615
|
composePrompt,
|
|
6985
6616
|
createUniqueUuid as createUniqueUuid2,
|
|
6986
|
-
logger as
|
|
6617
|
+
logger as logger3
|
|
6987
6618
|
} from "@elizaos/core";
|
|
6988
|
-
var wait = (minTime = 1e3, maxTime = 3e3) => {
|
|
6989
|
-
const waitTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
|
|
6990
|
-
return new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
6991
|
-
};
|
|
6992
|
-
async function buildConversationThread(tweet, client, maxReplies = 10) {
|
|
6993
|
-
const thread = [];
|
|
6994
|
-
const visited = /* @__PURE__ */ new Set();
|
|
6995
|
-
async function processThread(currentTweet, depth = 0) {
|
|
6996
|
-
logger2.debug("Processing tweet:", {
|
|
6997
|
-
id: currentTweet.id,
|
|
6998
|
-
inReplyToStatusId: currentTweet.inReplyToStatusId,
|
|
6999
|
-
depth
|
|
7000
|
-
});
|
|
7001
|
-
if (!currentTweet) {
|
|
7002
|
-
logger2.debug("No current tweet found for thread building");
|
|
7003
|
-
return;
|
|
7004
|
-
}
|
|
7005
|
-
if (depth >= maxReplies) {
|
|
7006
|
-
logger2.debug("Reached maximum reply depth", depth);
|
|
7007
|
-
return;
|
|
7008
|
-
}
|
|
7009
|
-
const memory = await client.runtime.getMemoryManager("messages").getMemoryById(createUniqueUuid2(this.runtime, currentTweet.id));
|
|
7010
|
-
if (!memory) {
|
|
7011
|
-
const roomId = createUniqueUuid2(
|
|
7012
|
-
this.runtime,
|
|
7013
|
-
currentTweet.conversationId
|
|
7014
|
-
);
|
|
7015
|
-
const entityId = createUniqueUuid2(this.runtime, currentTweet.userId);
|
|
7016
|
-
await client.runtime.ensureConnection({
|
|
7017
|
-
entityId,
|
|
7018
|
-
roomId,
|
|
7019
|
-
userName: currentTweet.username,
|
|
7020
|
-
name: currentTweet.name,
|
|
7021
|
-
source: "twitter",
|
|
7022
|
-
type: ChannelType2.GROUP
|
|
7023
|
-
});
|
|
7024
|
-
await client.runtime.getMemoryManager("messages").createMemory({
|
|
7025
|
-
id: createUniqueUuid2(this.runtime, currentTweet.id),
|
|
7026
|
-
agentId: client.runtime.agentId,
|
|
7027
|
-
content: {
|
|
7028
|
-
text: currentTweet.text,
|
|
7029
|
-
source: "twitter",
|
|
7030
|
-
url: currentTweet.permanentUrl,
|
|
7031
|
-
imageUrls: currentTweet.photos.map((p) => p.url) || [],
|
|
7032
|
-
inReplyTo: currentTweet.inReplyToStatusId ? createUniqueUuid2(this.runtime, currentTweet.inReplyToStatusId) : void 0
|
|
7033
|
-
},
|
|
7034
|
-
createdAt: currentTweet.timestamp * 1e3,
|
|
7035
|
-
roomId,
|
|
7036
|
-
entityId: currentTweet.userId === client.profile.id ? client.runtime.agentId : createUniqueUuid2(this.runtime, currentTweet.userId)
|
|
7037
|
-
});
|
|
7038
|
-
}
|
|
7039
|
-
if (visited.has(currentTweet.id)) {
|
|
7040
|
-
logger2.debug("Already visited tweet:", currentTweet.id);
|
|
7041
|
-
return;
|
|
7042
|
-
}
|
|
7043
|
-
visited.add(currentTweet.id);
|
|
7044
|
-
thread.unshift(currentTweet);
|
|
7045
|
-
logger2.debug("Current thread state:", {
|
|
7046
|
-
length: thread.length,
|
|
7047
|
-
currentDepth: depth,
|
|
7048
|
-
tweetId: currentTweet.id
|
|
7049
|
-
});
|
|
7050
|
-
if (currentTweet.inReplyToStatusId) {
|
|
7051
|
-
logger2.debug("Fetching parent tweet:", currentTweet.inReplyToStatusId);
|
|
7052
|
-
try {
|
|
7053
|
-
const parentTweet = await client.twitterClient.getTweet(
|
|
7054
|
-
currentTweet.inReplyToStatusId
|
|
7055
|
-
);
|
|
7056
|
-
if (parentTweet) {
|
|
7057
|
-
logger2.debug("Found parent tweet:", {
|
|
7058
|
-
id: parentTweet.id,
|
|
7059
|
-
text: parentTweet.text?.slice(0, 50)
|
|
7060
|
-
});
|
|
7061
|
-
await processThread(parentTweet, depth + 1);
|
|
7062
|
-
} else {
|
|
7063
|
-
logger2.debug(
|
|
7064
|
-
"No parent tweet found for:",
|
|
7065
|
-
currentTweet.inReplyToStatusId
|
|
7066
|
-
);
|
|
7067
|
-
}
|
|
7068
|
-
} catch (error) {
|
|
7069
|
-
logger2.error("Error fetching parent tweet:", {
|
|
7070
|
-
tweetId: currentTweet.inReplyToStatusId,
|
|
7071
|
-
error
|
|
7072
|
-
});
|
|
7073
|
-
}
|
|
7074
|
-
} else {
|
|
7075
|
-
logger2.debug("Reached end of reply chain at:", currentTweet.id);
|
|
7076
|
-
}
|
|
7077
|
-
}
|
|
7078
|
-
await processThread(tweet, 0);
|
|
7079
|
-
logger2.debug("Final thread built:", {
|
|
7080
|
-
totalTweets: thread.length,
|
|
7081
|
-
tweetIds: thread.map((t) => ({
|
|
7082
|
-
id: t.id,
|
|
7083
|
-
text: t.text?.slice(0, 50)
|
|
7084
|
-
}))
|
|
7085
|
-
});
|
|
7086
|
-
return thread;
|
|
7087
|
-
}
|
|
7088
6619
|
async function fetchMediaData(attachments) {
|
|
7089
6620
|
return Promise.all(
|
|
7090
6621
|
attachments.map(async (attachment) => {
|
|
@@ -7094,206 +6625,18 @@ async function fetchMediaData(attachments) {
|
|
|
7094
6625
|
throw new Error(`Failed to fetch file: ${attachment.url}`);
|
|
7095
6626
|
}
|
|
7096
6627
|
const mediaBuffer = Buffer.from(await response.arrayBuffer());
|
|
7097
|
-
const mediaType = attachment.contentType;
|
|
6628
|
+
const mediaType = attachment.contentType || "image/png";
|
|
7098
6629
|
return { data: mediaBuffer, mediaType };
|
|
7099
6630
|
}
|
|
7100
6631
|
if (fs.existsSync(attachment.url)) {
|
|
7101
|
-
const mediaBuffer = await fs.promises.readFile(
|
|
7102
|
-
|
|
7103
|
-
);
|
|
7104
|
-
const mediaType = attachment.contentType;
|
|
6632
|
+
const mediaBuffer = await fs.promises.readFile(path.resolve(attachment.url));
|
|
6633
|
+
const mediaType = attachment.contentType || "image/png";
|
|
7105
6634
|
return { data: mediaBuffer, mediaType };
|
|
7106
6635
|
}
|
|
7107
|
-
throw new Error(
|
|
7108
|
-
`File not found: ${attachment.url}. Make sure the path is correct.`
|
|
7109
|
-
);
|
|
6636
|
+
throw new Error(`File not found: ${attachment.url}. Make sure the path is correct.`);
|
|
7110
6637
|
})
|
|
7111
6638
|
);
|
|
7112
6639
|
}
|
|
7113
|
-
async function sendTweet(client, content, roomId, twitterUsername, inReplyTo) {
|
|
7114
|
-
const isLongTweet = content.text.length > 280 - 1;
|
|
7115
|
-
const tweetChunks = splitTweetContent(content.text, 280 - 1);
|
|
7116
|
-
const sentTweets = [];
|
|
7117
|
-
let previousTweetId = inReplyTo;
|
|
7118
|
-
for (const chunk of tweetChunks) {
|
|
7119
|
-
let mediaData = null;
|
|
7120
|
-
if (content.attachments && content.attachments.length > 0) {
|
|
7121
|
-
mediaData = await fetchMediaData(content.attachments);
|
|
7122
|
-
}
|
|
7123
|
-
const cleanChunk = deduplicateMentions(chunk.trim());
|
|
7124
|
-
const result = await client.requestQueue.add(
|
|
7125
|
-
async () => isLongTweet ? client.twitterClient.sendLongTweet(
|
|
7126
|
-
cleanChunk,
|
|
7127
|
-
previousTweetId,
|
|
7128
|
-
mediaData
|
|
7129
|
-
) : client.twitterClient.sendTweet(
|
|
7130
|
-
cleanChunk,
|
|
7131
|
-
previousTweetId,
|
|
7132
|
-
mediaData
|
|
7133
|
-
)
|
|
7134
|
-
);
|
|
7135
|
-
const body = await result.json();
|
|
7136
|
-
const tweetResult = isLongTweet ? body?.data?.notetweet_create?.tweet_results?.result : body?.data?.create_tweet?.tweet_results?.result;
|
|
7137
|
-
if (tweetResult) {
|
|
7138
|
-
const finalTweet = {
|
|
7139
|
-
id: tweetResult.rest_id,
|
|
7140
|
-
text: tweetResult.legacy.full_text,
|
|
7141
|
-
conversationId: tweetResult.legacy.conversation_id_str,
|
|
7142
|
-
timestamp: new Date(tweetResult.legacy.created_at).getTime() / 1e3,
|
|
7143
|
-
userId: tweetResult.legacy.user_id_str,
|
|
7144
|
-
inReplyToStatusId: tweetResult.legacy.in_reply_to_status_id_str,
|
|
7145
|
-
permanentUrl: `https://twitter.com/${twitterUsername}/status/${tweetResult.rest_id}`,
|
|
7146
|
-
hashtags: [],
|
|
7147
|
-
mentions: [],
|
|
7148
|
-
photos: [],
|
|
7149
|
-
thread: [],
|
|
7150
|
-
urls: [],
|
|
7151
|
-
videos: []
|
|
7152
|
-
};
|
|
7153
|
-
sentTweets.push(finalTweet);
|
|
7154
|
-
previousTweetId = finalTweet.id;
|
|
7155
|
-
} else {
|
|
7156
|
-
logger2.error("Error sending tweet chunk:", {
|
|
7157
|
-
chunk,
|
|
7158
|
-
response: body
|
|
7159
|
-
});
|
|
7160
|
-
}
|
|
7161
|
-
await wait(1e3, 2e3);
|
|
7162
|
-
}
|
|
7163
|
-
const memories = sentTweets.map((tweet) => ({
|
|
7164
|
-
id: createUniqueUuid2(client.runtime, tweet.id),
|
|
7165
|
-
agentId: client.runtime.agentId,
|
|
7166
|
-
entityId: client.runtime.agentId,
|
|
7167
|
-
content: {
|
|
7168
|
-
tweetId: tweet.id,
|
|
7169
|
-
text: tweet.text,
|
|
7170
|
-
source: "twitter",
|
|
7171
|
-
url: tweet.permanentUrl,
|
|
7172
|
-
imageUrls: tweet.photos.map((p) => p.url) || [],
|
|
7173
|
-
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid2(client.runtime, tweet.inReplyToStatusId) : void 0
|
|
7174
|
-
},
|
|
7175
|
-
roomId,
|
|
7176
|
-
createdAt: tweet.timestamp * 1e3
|
|
7177
|
-
}));
|
|
7178
|
-
return memories;
|
|
7179
|
-
}
|
|
7180
|
-
function splitTweetContent(content, maxLength) {
|
|
7181
|
-
const paragraphs = content.split("\n\n").map((p) => p.trim());
|
|
7182
|
-
const tweets = [];
|
|
7183
|
-
let currentTweet = "";
|
|
7184
|
-
for (const paragraph of paragraphs) {
|
|
7185
|
-
if (!paragraph) continue;
|
|
7186
|
-
if (`${currentTweet}
|
|
7187
|
-
|
|
7188
|
-
${paragraph}`.trim().length <= maxLength) {
|
|
7189
|
-
if (currentTweet) {
|
|
7190
|
-
currentTweet += `
|
|
7191
|
-
|
|
7192
|
-
${paragraph}`;
|
|
7193
|
-
} else {
|
|
7194
|
-
currentTweet = paragraph;
|
|
7195
|
-
}
|
|
7196
|
-
} else {
|
|
7197
|
-
if (currentTweet) {
|
|
7198
|
-
tweets.push(currentTweet.trim());
|
|
7199
|
-
}
|
|
7200
|
-
if (paragraph.length <= maxLength) {
|
|
7201
|
-
currentTweet = paragraph;
|
|
7202
|
-
} else {
|
|
7203
|
-
const chunks = splitParagraph(paragraph, maxLength);
|
|
7204
|
-
tweets.push(...chunks.slice(0, -1));
|
|
7205
|
-
currentTweet = chunks[chunks.length - 1];
|
|
7206
|
-
}
|
|
7207
|
-
}
|
|
7208
|
-
}
|
|
7209
|
-
if (currentTweet) {
|
|
7210
|
-
tweets.push(currentTweet.trim());
|
|
7211
|
-
}
|
|
7212
|
-
return tweets;
|
|
7213
|
-
}
|
|
7214
|
-
function extractUrls(paragraph) {
|
|
7215
|
-
const urlRegex = /https?:\/\/[^\s]+/g;
|
|
7216
|
-
const placeholderMap = /* @__PURE__ */ new Map();
|
|
7217
|
-
let urlIndex = 0;
|
|
7218
|
-
const textWithPlaceholders = paragraph.replace(urlRegex, (match) => {
|
|
7219
|
-
const placeholder = `<<URL_CONSIDERER_23_${urlIndex}>>`;
|
|
7220
|
-
placeholderMap.set(placeholder, match);
|
|
7221
|
-
urlIndex++;
|
|
7222
|
-
return placeholder;
|
|
7223
|
-
});
|
|
7224
|
-
return { textWithPlaceholders, placeholderMap };
|
|
7225
|
-
}
|
|
7226
|
-
function splitSentencesAndWords(text, maxLength) {
|
|
7227
|
-
const sentences = text.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [text];
|
|
7228
|
-
const chunks = [];
|
|
7229
|
-
let currentChunk = "";
|
|
7230
|
-
for (const sentence of sentences) {
|
|
7231
|
-
if (`${currentChunk} ${sentence}`.trim().length <= maxLength) {
|
|
7232
|
-
if (currentChunk) {
|
|
7233
|
-
currentChunk += ` ${sentence}`;
|
|
7234
|
-
} else {
|
|
7235
|
-
currentChunk = sentence;
|
|
7236
|
-
}
|
|
7237
|
-
} else {
|
|
7238
|
-
if (currentChunk) {
|
|
7239
|
-
chunks.push(currentChunk.trim());
|
|
7240
|
-
}
|
|
7241
|
-
if (sentence.length <= maxLength) {
|
|
7242
|
-
currentChunk = sentence;
|
|
7243
|
-
} else {
|
|
7244
|
-
const words = sentence.split(" ");
|
|
7245
|
-
currentChunk = "";
|
|
7246
|
-
for (const word of words) {
|
|
7247
|
-
if (`${currentChunk} ${word}`.trim().length <= maxLength) {
|
|
7248
|
-
if (currentChunk) {
|
|
7249
|
-
currentChunk += ` ${word}`;
|
|
7250
|
-
} else {
|
|
7251
|
-
currentChunk = word;
|
|
7252
|
-
}
|
|
7253
|
-
} else {
|
|
7254
|
-
if (currentChunk) {
|
|
7255
|
-
chunks.push(currentChunk.trim());
|
|
7256
|
-
}
|
|
7257
|
-
currentChunk = word;
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7260
|
-
}
|
|
7261
|
-
}
|
|
7262
|
-
}
|
|
7263
|
-
if (currentChunk) {
|
|
7264
|
-
chunks.push(currentChunk.trim());
|
|
7265
|
-
}
|
|
7266
|
-
return chunks;
|
|
7267
|
-
}
|
|
7268
|
-
function deduplicateMentions(paragraph) {
|
|
7269
|
-
const mentionRegex = /^@(\w+)(?:\s+@(\w+))*(\s+|$)/;
|
|
7270
|
-
const matches = paragraph.match(mentionRegex);
|
|
7271
|
-
if (!matches) {
|
|
7272
|
-
return paragraph;
|
|
7273
|
-
}
|
|
7274
|
-
let mentions = matches.slice(0, 1)[0].trim().split(" ");
|
|
7275
|
-
mentions = Array.from(new Set(mentions));
|
|
7276
|
-
const uniqueMentionsString = mentions.join(" ");
|
|
7277
|
-
const endOfMentions = paragraph.indexOf(matches[0]) + matches[0].length;
|
|
7278
|
-
return `${uniqueMentionsString} ${paragraph.slice(endOfMentions)}`;
|
|
7279
|
-
}
|
|
7280
|
-
function restoreUrls(chunks, placeholderMap) {
|
|
7281
|
-
return chunks.map((chunk) => {
|
|
7282
|
-
return chunk.replace(/<<URL_CONSIDERER_23_(\d+)>>/g, (match) => {
|
|
7283
|
-
const original = placeholderMap.get(match);
|
|
7284
|
-
return original || match;
|
|
7285
|
-
});
|
|
7286
|
-
});
|
|
7287
|
-
}
|
|
7288
|
-
function splitParagraph(paragraph, maxLength) {
|
|
7289
|
-
const { textWithPlaceholders, placeholderMap } = extractUrls(paragraph);
|
|
7290
|
-
const splittedChunks = splitSentencesAndWords(
|
|
7291
|
-
textWithPlaceholders,
|
|
7292
|
-
maxLength
|
|
7293
|
-
);
|
|
7294
|
-
const restoredChunks = restoreUrls(splittedChunks, placeholderMap);
|
|
7295
|
-
return restoredChunks;
|
|
7296
|
-
}
|
|
7297
6640
|
async function generateFiller(runtime, fillerType) {
|
|
7298
6641
|
try {
|
|
7299
6642
|
const prompt = composePrompt({
|
|
@@ -7311,12 +6654,12 @@ Only return the text, no additional formatting.
|
|
|
7311
6654
|
---
|
|
7312
6655
|
`
|
|
7313
6656
|
});
|
|
7314
|
-
const output = await runtime.useModel(
|
|
6657
|
+
const output = await runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
7315
6658
|
prompt
|
|
7316
6659
|
});
|
|
7317
6660
|
return output.trim();
|
|
7318
6661
|
} catch (err) {
|
|
7319
|
-
|
|
6662
|
+
logger3.error("[generateFiller] Error generating filler:", err);
|
|
7320
6663
|
return "";
|
|
7321
6664
|
}
|
|
7322
6665
|
}
|
|
@@ -7324,7 +6667,7 @@ async function speakFiller(runtime, sttTtsPlugin, fillerType, sleepAfterMs = 3e3
|
|
|
7324
6667
|
if (!sttTtsPlugin) return;
|
|
7325
6668
|
const text = await generateFiller(runtime, fillerType);
|
|
7326
6669
|
if (!text) return;
|
|
7327
|
-
|
|
6670
|
+
logger3.log(`[Space] Filler (${fillerType}) => ${text}`);
|
|
7328
6671
|
await sttTtsPlugin.speakText(text);
|
|
7329
6672
|
if (sleepAfterMs > 0) {
|
|
7330
6673
|
await new Promise((res) => setTimeout(res, sleepAfterMs));
|
|
@@ -7344,13 +6687,13 @@ Example:
|
|
|
7344
6687
|
---
|
|
7345
6688
|
`
|
|
7346
6689
|
});
|
|
7347
|
-
const response = await runtime.useModel(
|
|
6690
|
+
const response = await runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
7348
6691
|
prompt
|
|
7349
6692
|
});
|
|
7350
6693
|
const topics = response.split(",").map((t) => t.trim()).filter(Boolean);
|
|
7351
6694
|
return topics.length ? topics : ["Random Tech Chat", "AI Thoughts"];
|
|
7352
6695
|
} catch (err) {
|
|
7353
|
-
|
|
6696
|
+
logger3.error("[generateTopicsIfEmpty] GPT error =>", err);
|
|
7354
6697
|
return ["Random Tech Chat", "AI Thoughts"];
|
|
7355
6698
|
}
|
|
7356
6699
|
}
|
|
@@ -7359,32 +6702,20 @@ async function isAgentInSpace(client, spaceId) {
|
|
|
7359
6702
|
const agentName = client.state.TWITTER_USERNAME;
|
|
7360
6703
|
return space.participants.listeners.some(
|
|
7361
6704
|
(participant) => participant.twitter_screen_name === agentName
|
|
7362
|
-
) || space.participants.speakers.some(
|
|
7363
|
-
(participant) => participant.twitter_screen_name === agentName
|
|
7364
|
-
);
|
|
6705
|
+
) || space.participants.speakers.some((participant) => participant.twitter_screen_name === agentName);
|
|
7365
6706
|
}
|
|
7366
6707
|
|
|
7367
6708
|
// src/spaces.ts
|
|
7368
6709
|
var TwitterSpaceClient = class {
|
|
7369
|
-
runtime;
|
|
7370
|
-
client;
|
|
7371
|
-
twitterClient;
|
|
7372
|
-
currentSpace;
|
|
7373
|
-
spaceId;
|
|
7374
|
-
startedAt;
|
|
7375
|
-
checkInterval;
|
|
7376
|
-
lastSpaceEndedAt;
|
|
7377
|
-
sttTtsPlugin;
|
|
7378
|
-
spaceStatus = "idle" /* IDLE */;
|
|
7379
|
-
spaceParticipant = null;
|
|
7380
|
-
participantStatus = "listener" /* LISTENER */;
|
|
7381
|
-
/**
|
|
7382
|
-
* We now store an array of active speakers, not just 1
|
|
7383
|
-
*/
|
|
7384
|
-
activeSpeakers = [];
|
|
7385
|
-
speakerQueue = [];
|
|
7386
|
-
decisionOptions;
|
|
7387
6710
|
constructor(client, runtime) {
|
|
6711
|
+
this.spaceStatus = "idle" /* IDLE */;
|
|
6712
|
+
this.spaceParticipant = null;
|
|
6713
|
+
this.participantStatus = "listener" /* LISTENER */;
|
|
6714
|
+
/**
|
|
6715
|
+
* We now store an array of active speakers, not just 1
|
|
6716
|
+
*/
|
|
6717
|
+
this.activeSpeakers = [];
|
|
6718
|
+
this.speakerQueue = [];
|
|
7388
6719
|
this.client = client;
|
|
7389
6720
|
this.twitterClient = client.twitterClient;
|
|
7390
6721
|
this.runtime = runtime;
|
|
@@ -7405,7 +6736,7 @@ var TwitterSpaceClient = class {
|
|
|
7405
6736
|
* Periodic check to launch or manage space
|
|
7406
6737
|
*/
|
|
7407
6738
|
async startPeriodicSpaceCheck() {
|
|
7408
|
-
|
|
6739
|
+
logger4.log("[Space] Starting periodic check routine...");
|
|
7409
6740
|
const interval = 2e4;
|
|
7410
6741
|
const routine = async () => {
|
|
7411
6742
|
try {
|
|
@@ -7426,7 +6757,7 @@ var TwitterSpaceClient = class {
|
|
|
7426
6757
|
}
|
|
7427
6758
|
this.checkInterval = setTimeout(routine, interval);
|
|
7428
6759
|
} catch (error) {
|
|
7429
|
-
|
|
6760
|
+
logger4.error("[Space] Error in routine =>", error);
|
|
7430
6761
|
this.checkInterval = setTimeout(routine, interval);
|
|
7431
6762
|
}
|
|
7432
6763
|
};
|
|
@@ -7443,11 +6774,11 @@ var TwitterSpaceClient = class {
|
|
|
7443
6774
|
if (this.lastSpaceEndedAt) {
|
|
7444
6775
|
const minIntervalMs = (this.decisionOptions.minIntervalBetweenSpacesMinutes ?? 60) * 6e4;
|
|
7445
6776
|
if (now - this.lastSpaceEndedAt < minIntervalMs) {
|
|
7446
|
-
|
|
6777
|
+
logger4.log("[Space] Too soon since last space => skip");
|
|
7447
6778
|
return false;
|
|
7448
6779
|
}
|
|
7449
6780
|
}
|
|
7450
|
-
|
|
6781
|
+
logger4.log("[Space] Deciding to launch a new Space...");
|
|
7451
6782
|
return true;
|
|
7452
6783
|
}
|
|
7453
6784
|
async generateSpaceConfig() {
|
|
@@ -7467,7 +6798,7 @@ var TwitterSpaceClient = class {
|
|
|
7467
6798
|
};
|
|
7468
6799
|
}
|
|
7469
6800
|
async startSpace(config) {
|
|
7470
|
-
|
|
6801
|
+
logger4.log("[Space] Starting a new Twitter Space...");
|
|
7471
6802
|
try {
|
|
7472
6803
|
this.currentSpace = new Space(this.twitterClient);
|
|
7473
6804
|
this.spaceStatus = "idle" /* IDLE */;
|
|
@@ -7477,55 +6808,79 @@ var TwitterSpaceClient = class {
|
|
|
7477
6808
|
this.speakerQueue = [];
|
|
7478
6809
|
const broadcastInfo = await this.currentSpace.initialize(config);
|
|
7479
6810
|
this.spaceId = broadcastInfo.room_id;
|
|
7480
|
-
|
|
7481
|
-
|
|
6811
|
+
const userId = this.client.profile.id;
|
|
6812
|
+
const worldId = createUniqueUuid3(this.runtime, userId);
|
|
6813
|
+
const spaceRoomId = createUniqueUuid3(this.runtime, `${userId}-space-${this.spaceId}`);
|
|
6814
|
+
await this.runtime.ensureWorldExists({
|
|
6815
|
+
id: worldId,
|
|
6816
|
+
name: `${this.client.profile.username}'s Twitter`,
|
|
6817
|
+
agentId: this.runtime.agentId,
|
|
6818
|
+
serverId: userId,
|
|
6819
|
+
metadata: {
|
|
6820
|
+
ownership: { ownerId: userId },
|
|
6821
|
+
twitter: {
|
|
6822
|
+
username: this.client.profile.username,
|
|
6823
|
+
id: userId
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
});
|
|
6827
|
+
await this.runtime.ensureRoomExists({
|
|
6828
|
+
id: spaceRoomId,
|
|
6829
|
+
name: config.title || "Twitter Space",
|
|
6830
|
+
source: "twitter",
|
|
6831
|
+
type: ChannelType3.GROUP,
|
|
6832
|
+
channelId: this.spaceId,
|
|
6833
|
+
serverId: userId,
|
|
6834
|
+
worldId,
|
|
6835
|
+
metadata: {
|
|
6836
|
+
spaceInfo: {
|
|
6837
|
+
title: config.title,
|
|
6838
|
+
description: config.description,
|
|
6839
|
+
startedAt: Date.now(),
|
|
6840
|
+
mode: config.mode,
|
|
6841
|
+
languages: config.languages,
|
|
6842
|
+
isRecording: config.record
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
});
|
|
6846
|
+
if (this.runtime.getModel(ModelType3.TEXT_TO_SPEECH) && this.runtime.getModel(ModelType3.TRANSCRIPTION)) {
|
|
6847
|
+
logger4.log("[Space] Using SttTtsPlugin");
|
|
7482
6848
|
this.currentSpace.use(this.sttTtsPlugin, {
|
|
7483
6849
|
runtime: this.runtime,
|
|
7484
6850
|
spaceId: this.spaceId
|
|
7485
6851
|
});
|
|
7486
6852
|
}
|
|
7487
6853
|
if (this.decisionOptions.enableIdleMonitor) {
|
|
7488
|
-
|
|
6854
|
+
logger4.log("[Space] Using IdleMonitorPlugin");
|
|
7489
6855
|
this.currentSpace.use(
|
|
7490
|
-
new IdleMonitorPlugin(
|
|
7491
|
-
this.decisionOptions.idleKickTimeoutMs ?? 6e4,
|
|
7492
|
-
1e4
|
|
7493
|
-
)
|
|
6856
|
+
new IdleMonitorPlugin(this.decisionOptions.idleKickTimeoutMs ?? 6e4, 1e4)
|
|
7494
6857
|
);
|
|
7495
6858
|
}
|
|
7496
6859
|
this.spaceStatus = "hosting" /* HOSTING */;
|
|
7497
|
-
await this.twitterClient.sendTweet(
|
|
7498
|
-
broadcastInfo.share_url.replace("broadcasts", "spaces")
|
|
7499
|
-
);
|
|
7500
6860
|
const spaceUrl = broadcastInfo.share_url.replace("broadcasts", "spaces");
|
|
7501
|
-
|
|
6861
|
+
await this.twitterClient.sendTweet(spaceUrl);
|
|
6862
|
+
logger4.log(`[Space] Space started => ${spaceUrl}`);
|
|
7502
6863
|
await speakFiller(this.client.runtime, this.sttTtsPlugin, "WELCOME");
|
|
7503
6864
|
this.currentSpace.on("occupancyUpdate", (update) => {
|
|
7504
|
-
|
|
6865
|
+
logger4.log(`[Space] Occupancy => ${update.occupancy} participant(s).`);
|
|
7505
6866
|
});
|
|
7506
6867
|
this.currentSpace.on("speakerRequest", async (req) => {
|
|
7507
|
-
|
|
7508
|
-
`[Space] Speaker request from @${req.username} (${req.userId}).`
|
|
7509
|
-
);
|
|
6868
|
+
logger4.log(`[Space] Speaker request from @${req.username} (${req.userId}).`);
|
|
7510
6869
|
await this.handleSpeakerRequest(req);
|
|
7511
6870
|
});
|
|
7512
6871
|
this.currentSpace.on("idleTimeout", async (info) => {
|
|
7513
|
-
|
|
7514
|
-
await speakFiller(
|
|
7515
|
-
this.client.runtime,
|
|
7516
|
-
this.sttTtsPlugin,
|
|
7517
|
-
"IDLE_ENDING"
|
|
7518
|
-
);
|
|
6872
|
+
logger4.log(`[Space] idleTimeout => no audio for ${info.idleMs} ms.`);
|
|
6873
|
+
await speakFiller(this.client.runtime, this.sttTtsPlugin, "IDLE_ENDING");
|
|
7519
6874
|
await this.stopSpace();
|
|
7520
6875
|
});
|
|
7521
6876
|
process.on("SIGINT", async () => {
|
|
7522
|
-
|
|
6877
|
+
logger4.log("[Space] SIGINT => stopping space");
|
|
7523
6878
|
await speakFiller(this.client.runtime, this.sttTtsPlugin, "CLOSING");
|
|
7524
6879
|
await this.stopSpace();
|
|
7525
6880
|
process.exit(0);
|
|
7526
6881
|
});
|
|
7527
6882
|
} catch (error) {
|
|
7528
|
-
|
|
6883
|
+
logger4.error("[Space] Error launching Space =>", error);
|
|
7529
6884
|
this.spaceStatus = "idle" /* IDLE */;
|
|
7530
6885
|
throw error;
|
|
7531
6886
|
}
|
|
@@ -7536,9 +6891,7 @@ var TwitterSpaceClient = class {
|
|
|
7536
6891
|
async manageCurrentSpace() {
|
|
7537
6892
|
if (!this.spaceId || !this.currentSpace) return;
|
|
7538
6893
|
try {
|
|
7539
|
-
const audioSpace = await this.twitterClient.getAudioSpaceById(
|
|
7540
|
-
this.spaceId
|
|
7541
|
-
);
|
|
6894
|
+
const audioSpace = await this.twitterClient.getAudioSpaceById(this.spaceId);
|
|
7542
6895
|
const { participants } = audioSpace;
|
|
7543
6896
|
const numSpeakers = participants.speakers?.length || 0;
|
|
7544
6897
|
const totalListeners = participants.listeners?.length || 0;
|
|
@@ -7548,36 +6901,25 @@ var TwitterSpaceClient = class {
|
|
|
7548
6901
|
const speaker = this.activeSpeakers[i];
|
|
7549
6902
|
const elapsed = now - speaker.startTime;
|
|
7550
6903
|
if (elapsed > maxDur) {
|
|
7551
|
-
|
|
7552
|
-
`[Space] Speaker @${speaker.username} exceeded max duration => removing`
|
|
7553
|
-
);
|
|
6904
|
+
logger4.log(`[Space] Speaker @${speaker.username} exceeded max duration => removing`);
|
|
7554
6905
|
await this.removeSpeaker(speaker.userId);
|
|
7555
6906
|
this.activeSpeakers.splice(i, 1);
|
|
7556
|
-
await speakFiller(
|
|
7557
|
-
this.client.runtime,
|
|
7558
|
-
this.sttTtsPlugin,
|
|
7559
|
-
"SPEAKER_LEFT"
|
|
7560
|
-
);
|
|
6907
|
+
await speakFiller(this.client.runtime, this.sttTtsPlugin, "SPEAKER_LEFT");
|
|
7561
6908
|
}
|
|
7562
6909
|
}
|
|
7563
6910
|
await this.acceptSpeakersFromQueueIfNeeded();
|
|
7564
6911
|
if (numSpeakers > (this.decisionOptions.maxSpeakers ?? 1)) {
|
|
7565
|
-
|
|
6912
|
+
logger4.log("[Space] More than maxSpeakers => removing extras...");
|
|
7566
6913
|
await this.kickExtraSpeakers(participants.speakers);
|
|
7567
6914
|
}
|
|
7568
6915
|
const elapsedMinutes = (now - (this.startedAt || 0)) / 6e4;
|
|
7569
6916
|
if (elapsedMinutes > (this.decisionOptions.typicalDurationMinutes ?? 30) || numSpeakers === 0 && totalListeners === 0 && elapsedMinutes > 5) {
|
|
7570
|
-
|
|
7571
|
-
await speakFiller(
|
|
7572
|
-
this.client.runtime,
|
|
7573
|
-
this.sttTtsPlugin,
|
|
7574
|
-
"CLOSING",
|
|
7575
|
-
4e3
|
|
7576
|
-
);
|
|
6917
|
+
logger4.log("[Space] Condition met => stopping the Space...");
|
|
6918
|
+
await speakFiller(this.client.runtime, this.sttTtsPlugin, "CLOSING", 4e3);
|
|
7577
6919
|
await this.stopSpace();
|
|
7578
6920
|
}
|
|
7579
6921
|
} catch (error) {
|
|
7580
|
-
|
|
6922
|
+
logger4.error("[Space] Error in manageCurrentSpace =>", error);
|
|
7581
6923
|
}
|
|
7582
6924
|
}
|
|
7583
6925
|
/**
|
|
@@ -7598,11 +6940,11 @@ var TwitterSpaceClient = class {
|
|
|
7598
6940
|
const audioSpace = await this.twitterClient.getAudioSpaceById(this.spaceId);
|
|
7599
6941
|
const janusSpeakers = audioSpace?.participants?.speakers || [];
|
|
7600
6942
|
if (janusSpeakers.length < (this.decisionOptions.maxSpeakers ?? 1)) {
|
|
7601
|
-
|
|
6943
|
+
logger4.log(`[Space] Accepting speaker @${req.username} now`);
|
|
7602
6944
|
await speakFiller(this.client.runtime, this.sttTtsPlugin, "PRE_ACCEPT");
|
|
7603
6945
|
await this.acceptSpeaker(req);
|
|
7604
6946
|
} else {
|
|
7605
|
-
|
|
6947
|
+
logger4.log(`[Space] Adding speaker @${req.username} to the queue`);
|
|
7606
6948
|
this.speakerQueue.push(req);
|
|
7607
6949
|
}
|
|
7608
6950
|
}
|
|
@@ -7616,18 +6958,18 @@ var TwitterSpaceClient = class {
|
|
|
7616
6958
|
username: req.username,
|
|
7617
6959
|
startTime: Date.now()
|
|
7618
6960
|
});
|
|
7619
|
-
|
|
6961
|
+
logger4.log(`[Space] Speaker @${req.username} is now live`);
|
|
7620
6962
|
} catch (err) {
|
|
7621
|
-
|
|
6963
|
+
logger4.error(`[Space] Error approving speaker @${req.username}:`, err);
|
|
7622
6964
|
}
|
|
7623
6965
|
}
|
|
7624
6966
|
async removeSpeaker(userId) {
|
|
7625
6967
|
if (!this.currentSpace) return;
|
|
7626
6968
|
try {
|
|
7627
6969
|
await this.currentSpace.removeSpeaker(userId);
|
|
7628
|
-
|
|
6970
|
+
logger4.log(`[Space] Removed speaker userId=${userId}`);
|
|
7629
6971
|
} catch (error) {
|
|
7630
|
-
|
|
6972
|
+
logger4.error(`[Space] Error removing speaker userId=${userId} =>`, error);
|
|
7631
6973
|
}
|
|
7632
6974
|
}
|
|
7633
6975
|
/**
|
|
@@ -7639,7 +6981,7 @@ var TwitterSpaceClient = class {
|
|
|
7639
6981
|
const ms = this.decisionOptions.maxSpeakers ?? 1;
|
|
7640
6982
|
const extras = speakers.slice(ms);
|
|
7641
6983
|
for (const sp of extras) {
|
|
7642
|
-
|
|
6984
|
+
logger4.log(`[Space] Removing extra speaker => userId=${sp.user_id}`);
|
|
7643
6985
|
await this.removeSpeaker(sp.user_id);
|
|
7644
6986
|
const idx = this.activeSpeakers.findIndex((s) => s.userId === sp.user_id);
|
|
7645
6987
|
if (idx !== -1) {
|
|
@@ -7648,13 +6990,12 @@ var TwitterSpaceClient = class {
|
|
|
7648
6990
|
}
|
|
7649
6991
|
}
|
|
7650
6992
|
async stopSpace() {
|
|
7651
|
-
if (!this.currentSpace || this.spaceStatus !== "hosting" /* HOSTING */)
|
|
7652
|
-
return;
|
|
6993
|
+
if (!this.currentSpace || this.spaceStatus !== "hosting" /* HOSTING */) return;
|
|
7653
6994
|
try {
|
|
7654
|
-
|
|
6995
|
+
logger4.log("[Space] Stopping the current Space...");
|
|
7655
6996
|
await this.currentSpace.stop();
|
|
7656
6997
|
} catch (err) {
|
|
7657
|
-
|
|
6998
|
+
logger4.error("[Space] Error stopping Space =>", err);
|
|
7658
6999
|
} finally {
|
|
7659
7000
|
this.spaceStatus = "idle" /* IDLE */;
|
|
7660
7001
|
this.spaceId = void 0;
|
|
@@ -7667,7 +7008,7 @@ var TwitterSpaceClient = class {
|
|
|
7667
7008
|
}
|
|
7668
7009
|
async startParticipant(spaceId) {
|
|
7669
7010
|
if (this.spaceStatus !== "idle" /* IDLE */) {
|
|
7670
|
-
|
|
7011
|
+
logger4.warn("currently hosting/participating a space");
|
|
7671
7012
|
return null;
|
|
7672
7013
|
}
|
|
7673
7014
|
this.spaceParticipant = new SpaceParticipant(this.client.twitterClient, {
|
|
@@ -7681,7 +7022,7 @@ var TwitterSpaceClient = class {
|
|
|
7681
7022
|
this.spaceStatus = "participating" /* PARTICIPATING */;
|
|
7682
7023
|
return spaceId;
|
|
7683
7024
|
} catch (error) {
|
|
7684
|
-
|
|
7025
|
+
logger4.error(`failed to join space ${error}`);
|
|
7685
7026
|
return null;
|
|
7686
7027
|
}
|
|
7687
7028
|
}
|
|
@@ -7697,14 +7038,12 @@ var TwitterSpaceClient = class {
|
|
|
7697
7038
|
return;
|
|
7698
7039
|
}
|
|
7699
7040
|
if (this.participantStatus === "listener" /* LISTENER */) {
|
|
7700
|
-
|
|
7701
|
-
"[SpaceParticipant] Checking if we should request to speak..."
|
|
7702
|
-
);
|
|
7041
|
+
logger4.log("[SpaceParticipant] Checking if we should request to speak...");
|
|
7703
7042
|
this.participantStatus = "pending" /* PENDING */;
|
|
7704
7043
|
const { sessionUUID } = await this.spaceParticipant.requestSpeaker();
|
|
7705
7044
|
const handleSpeakerRemove = async (evt) => {
|
|
7706
7045
|
if (evt.sessionUUID === sessionUUID) {
|
|
7707
|
-
|
|
7046
|
+
logger4.debug("[SpaceParticipant] Speaker removed:", evt);
|
|
7708
7047
|
try {
|
|
7709
7048
|
await this.spaceParticipant.removeFromSpeaker();
|
|
7710
7049
|
} catch (err) {
|
|
@@ -7726,31 +7065,20 @@ var TwitterSpaceClient = class {
|
|
|
7726
7065
|
this.participantStatus = "listener" /* LISTENER */;
|
|
7727
7066
|
try {
|
|
7728
7067
|
await this.spaceParticipant.cancelSpeakerRequest();
|
|
7729
|
-
|
|
7730
|
-
"[SpaceParticipant] Speaker request canceled after timeout or error."
|
|
7731
|
-
);
|
|
7068
|
+
logger4.debug("[SpaceParticipant] Speaker request canceled after timeout or error.");
|
|
7732
7069
|
} catch (cancelErr) {
|
|
7733
|
-
console.error(
|
|
7734
|
-
"[SpaceParticipant] Could not cancel the request =>",
|
|
7735
|
-
cancelErr
|
|
7736
|
-
);
|
|
7070
|
+
console.error("[SpaceParticipant] Could not cancel the request =>", cancelErr);
|
|
7737
7071
|
}
|
|
7738
7072
|
});
|
|
7739
7073
|
}
|
|
7740
7074
|
}
|
|
7741
7075
|
async stopParticipant() {
|
|
7742
|
-
if (!this.spaceParticipant || this.spaceStatus !== "participating" /* PARTICIPATING */)
|
|
7743
|
-
return;
|
|
7076
|
+
if (!this.spaceParticipant || this.spaceStatus !== "participating" /* PARTICIPATING */) return;
|
|
7744
7077
|
try {
|
|
7745
|
-
|
|
7746
|
-
"[SpaceParticipant] Stopping the current space participant..."
|
|
7747
|
-
);
|
|
7078
|
+
logger4.log("[SpaceParticipant] Stopping the current space participant...");
|
|
7748
7079
|
await this.spaceParticipant.leaveSpace();
|
|
7749
7080
|
} catch (err) {
|
|
7750
|
-
|
|
7751
|
-
"[SpaceParticipant] Error stopping space participant =>",
|
|
7752
|
-
err
|
|
7753
|
-
);
|
|
7081
|
+
logger4.error("[SpaceParticipant] Error stopping space participant =>", err);
|
|
7754
7082
|
} finally {
|
|
7755
7083
|
this.spaceStatus = "idle" /* IDLE */;
|
|
7756
7084
|
this.participantStatus = "listener" /* LISTENER */;
|
|
@@ -7771,7 +7099,7 @@ var TwitterSpaceClient = class {
|
|
|
7771
7099
|
participant.off("newSpeakerAccepted", handler);
|
|
7772
7100
|
try {
|
|
7773
7101
|
await participant.becomeSpeaker();
|
|
7774
|
-
|
|
7102
|
+
logger4.debug("[SpaceParticipant] Successfully became speaker!");
|
|
7775
7103
|
resolve();
|
|
7776
7104
|
} catch (err) {
|
|
7777
7105
|
reject(err);
|
|
@@ -7816,13 +7144,13 @@ var spaceJoin_default = {
|
|
|
7816
7144
|
description: "Join a Twitter Space to participate in live audio conversations.",
|
|
7817
7145
|
handler: async (runtime, message, state, _options, callback, responses) => {
|
|
7818
7146
|
if (!state) {
|
|
7819
|
-
|
|
7147
|
+
logger5.error("State is not available.");
|
|
7820
7148
|
return false;
|
|
7821
7149
|
}
|
|
7822
7150
|
for (const response of responses) {
|
|
7823
7151
|
await callback(response.content);
|
|
7824
7152
|
}
|
|
7825
|
-
const manager = runtime.getService(
|
|
7153
|
+
const manager = runtime.getService(ServiceType.TWITTER);
|
|
7826
7154
|
if (!manager) {
|
|
7827
7155
|
throw new Error("Twitter client manager not found");
|
|
7828
7156
|
}
|
|
@@ -7831,16 +7159,16 @@ var spaceJoin_default = {
|
|
|
7831
7159
|
const client = manager.clients.get(clientKey).client;
|
|
7832
7160
|
const spaceManager = manager.clients.get(clientKey).space;
|
|
7833
7161
|
if (!spaceManager) {
|
|
7834
|
-
|
|
7162
|
+
logger5.error("space action - no space manager found");
|
|
7835
7163
|
return false;
|
|
7836
7164
|
}
|
|
7837
7165
|
if (spaceManager.spaceStatus !== "idle" /* IDLE */) {
|
|
7838
|
-
|
|
7166
|
+
logger5.warn("currently hosting/participating a space");
|
|
7839
7167
|
return false;
|
|
7840
7168
|
}
|
|
7841
7169
|
const tweet = message.content.tweet;
|
|
7842
7170
|
if (!tweet) {
|
|
7843
|
-
|
|
7171
|
+
logger5.warn("space action - no tweet found in message");
|
|
7844
7172
|
return false;
|
|
7845
7173
|
}
|
|
7846
7174
|
async function joinSpaceByUrls(tweet2) {
|
|
@@ -7856,7 +7184,7 @@ var spaceJoin_default = {
|
|
|
7856
7184
|
return !!spaceJoined2;
|
|
7857
7185
|
}
|
|
7858
7186
|
} catch (error) {
|
|
7859
|
-
|
|
7187
|
+
logger5.error("Error joining Twitter Space:", error);
|
|
7860
7188
|
}
|
|
7861
7189
|
}
|
|
7862
7190
|
}
|
|
@@ -7871,7 +7199,7 @@ var spaceJoin_default = {
|
|
|
7871
7199
|
}
|
|
7872
7200
|
}
|
|
7873
7201
|
} catch (error) {
|
|
7874
|
-
|
|
7202
|
+
logger5.error(`Error fetching tweets for ${userName}:`, error);
|
|
7875
7203
|
}
|
|
7876
7204
|
return false;
|
|
7877
7205
|
}
|
|
@@ -7927,15 +7255,16 @@ var spaceJoin_default = {
|
|
|
7927
7255
|
};
|
|
7928
7256
|
|
|
7929
7257
|
// src/base.ts
|
|
7930
|
-
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
7931
7258
|
import {
|
|
7932
|
-
ChannelType as
|
|
7933
|
-
createUniqueUuid as
|
|
7934
|
-
logger as
|
|
7259
|
+
ChannelType as ChannelType4,
|
|
7260
|
+
createUniqueUuid as createUniqueUuid4,
|
|
7261
|
+
logger as logger6
|
|
7935
7262
|
} from "@elizaos/core";
|
|
7936
7263
|
var RequestQueue = class {
|
|
7937
|
-
|
|
7938
|
-
|
|
7264
|
+
constructor() {
|
|
7265
|
+
this.queue = [];
|
|
7266
|
+
this.processing = false;
|
|
7267
|
+
}
|
|
7939
7268
|
/**
|
|
7940
7269
|
* Asynchronously adds a request to the queue, then processes the queue.
|
|
7941
7270
|
*
|
|
@@ -7998,14 +7327,22 @@ var RequestQueue = class {
|
|
|
7998
7327
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7999
7328
|
}
|
|
8000
7329
|
};
|
|
8001
|
-
var
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
7330
|
+
var _ClientBase = class _ClientBase {
|
|
7331
|
+
constructor(runtime, state) {
|
|
7332
|
+
this.lastCheckedTweetId = null;
|
|
7333
|
+
this.temperature = 0.5;
|
|
7334
|
+
this.requestQueue = new RequestQueue();
|
|
7335
|
+
this.callback = null;
|
|
7336
|
+
this.runtime = runtime;
|
|
7337
|
+
this.state = state;
|
|
7338
|
+
const username = state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
7339
|
+
if (_ClientBase._twitterClients[username]) {
|
|
7340
|
+
this.twitterClient = _ClientBase._twitterClients[username];
|
|
7341
|
+
} else {
|
|
7342
|
+
this.twitterClient = new Client();
|
|
7343
|
+
_ClientBase._twitterClients[username] = this.twitterClient;
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
8009
7346
|
/**
|
|
8010
7347
|
* Caches a tweet in the database.
|
|
8011
7348
|
*
|
|
@@ -8017,7 +7354,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8017
7354
|
console.warn("Tweet is undefined, skipping cache");
|
|
8018
7355
|
return;
|
|
8019
7356
|
}
|
|
8020
|
-
this.runtime.
|
|
7357
|
+
this.runtime.setCache(`twitter/tweets/${tweet.id}`, tweet);
|
|
8021
7358
|
}
|
|
8022
7359
|
/**
|
|
8023
7360
|
* Retrieves a cached tweet by its ID.
|
|
@@ -8025,7 +7362,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8025
7362
|
* @returns {Promise<Tweet | undefined>} A Promise that resolves to the cached tweet, or undefined if the tweet is not found in the cache.
|
|
8026
7363
|
*/
|
|
8027
7364
|
async getCachedTweet(tweetId) {
|
|
8028
|
-
const cached = await this.runtime.
|
|
7365
|
+
const cached = await this.runtime.getCache(`twitter/tweets/${tweetId}`);
|
|
8029
7366
|
if (!cached) {
|
|
8030
7367
|
return void 0;
|
|
8031
7368
|
}
|
|
@@ -8043,13 +7380,10 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8043
7380
|
if (cachedTweet) {
|
|
8044
7381
|
return cachedTweet;
|
|
8045
7382
|
}
|
|
8046
|
-
const tweet = await this.requestQueue.add(
|
|
8047
|
-
() => this.twitterClient.getTweet(tweetId)
|
|
8048
|
-
);
|
|
7383
|
+
const tweet = await this.requestQueue.add(() => this.twitterClient.getTweet(tweetId));
|
|
8049
7384
|
await this.cacheTweet(tweet);
|
|
8050
7385
|
return tweet;
|
|
8051
7386
|
}
|
|
8052
|
-
callback = null;
|
|
8053
7387
|
/**
|
|
8054
7388
|
* This method is called when the application is ready.
|
|
8055
7389
|
* It throws an error indicating that it is not implemented in the base class
|
|
@@ -8072,17 +7406,13 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8072
7406
|
parseTweet(raw, depth = 0, maxDepth = 3) {
|
|
8073
7407
|
const canRecurse = depth < maxDepth;
|
|
8074
7408
|
const quotedStatus = raw.quoted_status_result?.result && canRecurse ? this.parseTweet(raw.quoted_status_result.result, depth + 1, maxDepth) : void 0;
|
|
8075
|
-
const retweetedStatus = raw.retweeted_status_result?.result && canRecurse ? this.parseTweet(
|
|
8076
|
-
raw.retweeted_status_result.result,
|
|
8077
|
-
depth + 1,
|
|
8078
|
-
maxDepth
|
|
8079
|
-
) : void 0;
|
|
7409
|
+
const retweetedStatus = raw.retweeted_status_result?.result && canRecurse ? this.parseTweet(raw.retweeted_status_result.result, depth + 1, maxDepth) : void 0;
|
|
8080
7410
|
const t = {
|
|
8081
7411
|
bookmarkCount: raw.bookmarkCount ?? raw.legacy?.bookmark_count ?? void 0,
|
|
8082
7412
|
conversationId: raw.conversationId ?? raw.legacy?.conversation_id_str,
|
|
8083
7413
|
hashtags: raw.hashtags ?? raw.legacy?.entities?.hashtags ?? [],
|
|
8084
7414
|
html: raw.html,
|
|
8085
|
-
id: raw.id ?? raw.rest_id ?? raw.id_str ?? void 0,
|
|
7415
|
+
id: raw.id ?? raw.rest_id ?? raw.legacy.id_str ?? raw.id_str ?? void 0,
|
|
8086
7416
|
inReplyToStatus: raw.inReplyToStatus,
|
|
8087
7417
|
inReplyToStatusId: raw.inReplyToStatusId ?? raw.legacy?.in_reply_to_status_id_str ?? void 0,
|
|
8088
7418
|
isQuoted: raw.legacy?.is_quote_status === true,
|
|
@@ -8096,7 +7426,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8096
7426
|
mentions: raw.mentions ?? raw.legacy?.entities?.user_mentions ?? [],
|
|
8097
7427
|
permanentUrl: raw.permanentUrl ?? (raw.core?.user_results?.result?.legacy?.screen_name && raw.rest_id ? `https://x.com/${raw.core?.user_results?.result?.legacy?.screen_name}/status/${raw.rest_id}` : void 0),
|
|
8098
7428
|
photos: raw.photos ?? (raw.legacy?.entities?.media?.filter((media) => media.type === "photo").map((media) => ({
|
|
8099
|
-
id: media.id_str,
|
|
7429
|
+
id: media.id_str || media.rest_id || media.legacy.id_str,
|
|
8100
7430
|
url: media.media_url_https,
|
|
8101
7431
|
alt_text: media.alt_text
|
|
8102
7432
|
})) || []),
|
|
@@ -8116,28 +7446,14 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8116
7446
|
urls: raw.urls ?? raw.legacy?.entities?.urls ?? [],
|
|
8117
7447
|
userId: raw.userId ?? raw.legacy?.user_id_str ?? void 0,
|
|
8118
7448
|
username: raw.username ?? raw.core?.user_results?.result?.legacy?.screen_name ?? void 0,
|
|
8119
|
-
videos: raw.videos ?? raw.legacy?.entities?.media?.filter(
|
|
8120
|
-
(media) => media.type === "video"
|
|
8121
|
-
) ?? [],
|
|
7449
|
+
videos: raw.videos ?? raw.legacy?.entities?.media?.filter((media) => media.type === "video") ?? [],
|
|
8122
7450
|
views: raw.views?.count ? Number(raw.views.count) : 0,
|
|
8123
7451
|
sensitiveContent: raw.sensitiveContent
|
|
8124
7452
|
};
|
|
8125
7453
|
return t;
|
|
8126
7454
|
}
|
|
8127
|
-
state;
|
|
8128
|
-
constructor(runtime, state) {
|
|
8129
|
-
super();
|
|
8130
|
-
this.runtime = runtime;
|
|
8131
|
-
this.state = state;
|
|
8132
|
-
const username = state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
8133
|
-
if (_ClientBase._twitterClients[username]) {
|
|
8134
|
-
this.twitterClient = _ClientBase._twitterClients[username];
|
|
8135
|
-
} else {
|
|
8136
|
-
this.twitterClient = new Client();
|
|
8137
|
-
_ClientBase._twitterClients[username] = this.twitterClient;
|
|
8138
|
-
}
|
|
8139
|
-
}
|
|
8140
7455
|
async init() {
|
|
7456
|
+
await this.runtime.ensureAgentExists(this.runtime.character);
|
|
8141
7457
|
const username = this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
8142
7458
|
const password = this.state?.TWITTER_PASSWORD || this.runtime.getSetting("TWITTER_PASSWORD");
|
|
8143
7459
|
const email = this.state?.TWITTER_EMAIL || this.runtime.getSetting("TWITTER_EMAIL");
|
|
@@ -8147,68 +7463,58 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8147
7463
|
if (!username) missing.push("TWITTER_USERNAME");
|
|
8148
7464
|
if (!password) missing.push("TWITTER_PASSWORD");
|
|
8149
7465
|
if (!email) missing.push("TWITTER_EMAIL");
|
|
8150
|
-
throw new Error(
|
|
8151
|
-
`Missing required Twitter credentials: ${missing.join(", ")}`
|
|
8152
|
-
);
|
|
7466
|
+
throw new Error(`Missing required Twitter credentials: ${missing.join(", ")}`);
|
|
8153
7467
|
}
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
throw new Error("Twitter username not configured");
|
|
8159
|
-
}
|
|
8160
|
-
const authToken = this.state?.TWITTER_COOKIES_AUTH_TOKEN || this.runtime.getSetting("TWITTER_COOKIES_AUTH_TOKEN");
|
|
8161
|
-
const ct0 = this.state?.TWITTER_COOKIES_CT0 || this.runtime.getSetting("TWITTER_COOKIES_CT0");
|
|
8162
|
-
const guestId = this.state?.TWITTER_COOKIES_GUEST_ID || this.runtime.getSetting("TWITTER_COOKIES_GUEST_ID");
|
|
8163
|
-
const createTwitterCookies = (authToken2, ct02, guestId2) => authToken2 && ct02 && guestId2 ? [
|
|
8164
|
-
{ key: "auth_token", value: authToken2, domain: ".twitter.com" },
|
|
8165
|
-
{ key: "ct0", value: ct02, domain: ".twitter.com" },
|
|
8166
|
-
{ key: "guest_id", value: guestId2, domain: ".twitter.com" }
|
|
8167
|
-
] : null;
|
|
8168
|
-
const cachedCookies = await this.getCachedCookies(username) || createTwitterCookies(authToken, ct0, guestId);
|
|
8169
|
-
if (cachedCookies) {
|
|
8170
|
-
logger5.info("Using cached cookies");
|
|
8171
|
-
await this.setCookiesFromArray(cachedCookies);
|
|
8172
|
-
}
|
|
8173
|
-
logger5.log("Waiting for Twitter login");
|
|
8174
|
-
while (retries > 0) {
|
|
7468
|
+
const maxRetries = 3;
|
|
7469
|
+
let retryCount = 0;
|
|
7470
|
+
let lastError = null;
|
|
7471
|
+
while (retryCount < maxRetries) {
|
|
8175
7472
|
try {
|
|
7473
|
+
const authToken = this.state?.TWITTER_COOKIES_AUTH_TOKEN || this.runtime.getSetting("TWITTER_COOKIES_AUTH_TOKEN");
|
|
7474
|
+
const ct0 = this.state?.TWITTER_COOKIES_CT0 || this.runtime.getSetting("TWITTER_COOKIES_CT0");
|
|
7475
|
+
const guestId = this.state?.TWITTER_COOKIES_GUEST_ID || this.runtime.getSetting("TWITTER_COOKIES_GUEST_ID");
|
|
7476
|
+
const createTwitterCookies = (authToken2, ct02, guestId2) => authToken2 && ct02 && guestId2 ? [
|
|
7477
|
+
{ key: "auth_token", value: authToken2, domain: ".twitter.com" },
|
|
7478
|
+
{ key: "ct0", value: ct02, domain: ".twitter.com" },
|
|
7479
|
+
{ key: "guest_id", value: guestId2, domain: ".twitter.com" }
|
|
7480
|
+
] : null;
|
|
7481
|
+
const cachedCookies = await this.getCachedCookies(username) || createTwitterCookies(authToken, ct0, guestId);
|
|
7482
|
+
if (cachedCookies) {
|
|
7483
|
+
logger6.info("Using cached cookies");
|
|
7484
|
+
await this.setCookiesFromArray(cachedCookies);
|
|
7485
|
+
}
|
|
7486
|
+
logger6.log("Waiting for Twitter login");
|
|
8176
7487
|
if (await this.twitterClient.isLoggedIn()) {
|
|
8177
|
-
|
|
7488
|
+
logger6.info("Successfully logged in.");
|
|
8178
7489
|
break;
|
|
8179
7490
|
}
|
|
8180
|
-
await this.twitterClient.login(
|
|
8181
|
-
username,
|
|
8182
|
-
password,
|
|
8183
|
-
email,
|
|
8184
|
-
twitter2faSecret
|
|
8185
|
-
);
|
|
7491
|
+
await this.twitterClient.login(username, password, email, twitter2faSecret);
|
|
8186
7492
|
if (await this.twitterClient.isLoggedIn()) {
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
await this.cacheCookies(
|
|
8190
|
-
username,
|
|
8191
|
-
await this.twitterClient.getCookies()
|
|
8192
|
-
);
|
|
7493
|
+
logger6.info("Successfully logged in.");
|
|
7494
|
+
logger6.info("Caching cookies");
|
|
7495
|
+
await this.cacheCookies(username, await this.twitterClient.getCookies());
|
|
8193
7496
|
break;
|
|
8194
7497
|
}
|
|
8195
7498
|
} catch (error) {
|
|
8196
|
-
|
|
7499
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
7500
|
+
logger6.error(`Login attempt ${retryCount + 1} failed: ${lastError.message}`);
|
|
7501
|
+
retryCount++;
|
|
7502
|
+
if (retryCount < maxRetries) {
|
|
7503
|
+
const delay = 2 ** retryCount * 1e3;
|
|
7504
|
+
logger6.info(`Retrying in ${delay / 1e3} seconds...`);
|
|
7505
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7506
|
+
}
|
|
8197
7507
|
}
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
7508
|
+
}
|
|
7509
|
+
if (retryCount >= maxRetries) {
|
|
7510
|
+
throw new Error(
|
|
7511
|
+
`Twitter login failed after ${maxRetries} attempts. Last error: ${lastError?.message}`
|
|
8201
7512
|
);
|
|
8202
|
-
if (retries === 0) {
|
|
8203
|
-
logger5.error("Max retries reached. Exiting login process.");
|
|
8204
|
-
throw new Error("Twitter login failed after maximum retries.");
|
|
8205
|
-
}
|
|
8206
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
8207
7513
|
}
|
|
8208
7514
|
this.profile = await this.fetchProfile(username);
|
|
8209
7515
|
if (this.profile) {
|
|
8210
|
-
|
|
8211
|
-
|
|
7516
|
+
logger6.log("Twitter user ID:", this.profile.id);
|
|
7517
|
+
logger6.log("Twitter loaded:", JSON.stringify(this.profile, null, 10));
|
|
8212
7518
|
this.profile = {
|
|
8213
7519
|
id: this.profile.id,
|
|
8214
7520
|
username: this.profile.username,
|
|
@@ -8223,20 +7529,16 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8223
7529
|
await this.populateTimeline();
|
|
8224
7530
|
}
|
|
8225
7531
|
async fetchOwnPosts(count) {
|
|
8226
|
-
|
|
8227
|
-
const homeTimeline = await this.twitterClient.getUserTweets(
|
|
8228
|
-
this.profile.id,
|
|
8229
|
-
count
|
|
8230
|
-
);
|
|
7532
|
+
logger6.debug("fetching own posts");
|
|
7533
|
+
const homeTimeline = await this.twitterClient.getUserTweets(this.profile.id, count);
|
|
8231
7534
|
return homeTimeline.tweets.map((t) => this.parseTweet(t));
|
|
8232
7535
|
}
|
|
8233
7536
|
/**
|
|
8234
7537
|
* Fetch timeline for twitter account, optionally only from followed accounts
|
|
8235
7538
|
*/
|
|
8236
7539
|
async fetchHomeTimeline(count, following) {
|
|
8237
|
-
|
|
7540
|
+
logger6.debug("fetching home timeline");
|
|
8238
7541
|
const homeTimeline = following ? await this.twitterClient.fetchFollowingTimeline(count, []) : await this.twitterClient.fetchHomeTimeline(count, []);
|
|
8239
|
-
logger5.debug(homeTimeline);
|
|
8240
7542
|
const processedTimeline = homeTimeline.filter((t) => t.__typename !== "TweetWithVisibilityResults").map((tweet) => this.parseTweet(tweet));
|
|
8241
7543
|
return processedTimeline;
|
|
8242
7544
|
}
|
|
@@ -8248,87 +7550,97 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8248
7550
|
try {
|
|
8249
7551
|
const result = await this.requestQueue.add(
|
|
8250
7552
|
async () => await Promise.race([
|
|
8251
|
-
this.twitterClient.fetchSearchTweets(
|
|
8252
|
-
query,
|
|
8253
|
-
maxTweets,
|
|
8254
|
-
searchMode,
|
|
8255
|
-
cursor
|
|
8256
|
-
),
|
|
7553
|
+
this.twitterClient.fetchSearchTweets(query, maxTweets, searchMode, cursor),
|
|
8257
7554
|
timeoutPromise
|
|
8258
7555
|
])
|
|
8259
7556
|
);
|
|
8260
7557
|
return result ?? { tweets: [] };
|
|
8261
7558
|
} catch (error) {
|
|
8262
|
-
|
|
7559
|
+
logger6.error("Error fetching search tweets:", error);
|
|
8263
7560
|
return { tweets: [] };
|
|
8264
7561
|
}
|
|
8265
7562
|
} catch (error) {
|
|
8266
|
-
|
|
7563
|
+
logger6.error("Error fetching search tweets:", error);
|
|
8267
7564
|
return { tweets: [] };
|
|
8268
7565
|
}
|
|
8269
7566
|
}
|
|
8270
7567
|
async populateTimeline() {
|
|
8271
|
-
|
|
7568
|
+
logger6.debug("populating timeline...");
|
|
8272
7569
|
const cachedTimeline = await this.getCachedTimeline();
|
|
8273
7570
|
if (cachedTimeline) {
|
|
8274
|
-
const existingMemories2 = await this.runtime.
|
|
7571
|
+
const existingMemories2 = await this.runtime.getMemoriesByRoomIds({
|
|
7572
|
+
tableName: "messages",
|
|
8275
7573
|
roomIds: cachedTimeline.map(
|
|
8276
|
-
(tweet) =>
|
|
7574
|
+
(tweet) => createUniqueUuid4(this.runtime, tweet.conversationId)
|
|
8277
7575
|
)
|
|
8278
7576
|
});
|
|
8279
|
-
const existingMemoryIds2 = new Set(
|
|
8280
|
-
existingMemories2.map((memory) => memory.id.toString())
|
|
8281
|
-
);
|
|
7577
|
+
const existingMemoryIds2 = new Set(existingMemories2.map((memory) => memory.id.toString()));
|
|
8282
7578
|
const someCachedTweetsExist = cachedTimeline.some(
|
|
8283
|
-
(tweet) => existingMemoryIds2.has(
|
|
7579
|
+
(tweet) => existingMemoryIds2.has(createUniqueUuid4(this.runtime, tweet.id))
|
|
8284
7580
|
);
|
|
8285
7581
|
if (someCachedTweetsExist) {
|
|
8286
7582
|
const tweetsToSave2 = cachedTimeline.filter(
|
|
8287
|
-
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(
|
|
7583
|
+
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(createUniqueUuid4(this.runtime, tweet.id))
|
|
8288
7584
|
);
|
|
8289
7585
|
for (const tweet of tweetsToSave2) {
|
|
8290
|
-
|
|
7586
|
+
logger6.log("Saving Tweet", tweet.id);
|
|
8291
7587
|
if (tweet.userId === this.profile.id) {
|
|
8292
7588
|
continue;
|
|
8293
7589
|
}
|
|
8294
|
-
const
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
7590
|
+
const worldId = createUniqueUuid4(this.runtime, tweet.userId);
|
|
7591
|
+
await this.runtime.ensureWorldExists({
|
|
7592
|
+
id: worldId,
|
|
7593
|
+
name: `${tweet.username}'s Twitter`,
|
|
7594
|
+
agentId: this.runtime.agentId,
|
|
7595
|
+
serverId: tweet.userId,
|
|
7596
|
+
metadata: {
|
|
7597
|
+
ownership: { ownerId: tweet.userId },
|
|
7598
|
+
twitter: {
|
|
7599
|
+
username: tweet.username,
|
|
7600
|
+
id: tweet.userId
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
});
|
|
7604
|
+
const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
|
|
7605
|
+
await this.runtime.ensureRoomExists({
|
|
7606
|
+
id: roomId,
|
|
7607
|
+
name: `${tweet.username}'s Thread`,
|
|
7608
|
+
source: "twitter",
|
|
7609
|
+
type: ChannelType4.FEED,
|
|
7610
|
+
channelId: tweet.conversationId,
|
|
7611
|
+
serverId: tweet.userId,
|
|
7612
|
+
worldId
|
|
7613
|
+
});
|
|
7614
|
+
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId : createUniqueUuid4(this.runtime, tweet.userId);
|
|
8299
7615
|
await this.runtime.ensureConnection({
|
|
8300
7616
|
entityId,
|
|
8301
7617
|
roomId,
|
|
8302
7618
|
userName: tweet.username,
|
|
8303
7619
|
name: tweet.name,
|
|
8304
7620
|
source: "twitter",
|
|
8305
|
-
type:
|
|
7621
|
+
type: ChannelType4.FEED,
|
|
7622
|
+
worldId
|
|
8306
7623
|
});
|
|
8307
7624
|
const content = {
|
|
8308
7625
|
text: tweet.text,
|
|
8309
7626
|
url: tweet.permanentUrl,
|
|
8310
7627
|
source: "twitter",
|
|
8311
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
7628
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
|
|
8312
7629
|
};
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
roomId,
|
|
8325
|
-
createdAt: tweet.timestamp * 1e3
|
|
8326
|
-
});
|
|
7630
|
+
await this.runtime.createMemory(
|
|
7631
|
+
{
|
|
7632
|
+
id: createUniqueUuid4(this.runtime, tweet.id),
|
|
7633
|
+
entityId,
|
|
7634
|
+
content,
|
|
7635
|
+
agentId: this.runtime.agentId,
|
|
7636
|
+
roomId,
|
|
7637
|
+
createdAt: tweet.timestamp * 1e3
|
|
7638
|
+
},
|
|
7639
|
+
"messages"
|
|
7640
|
+
);
|
|
8327
7641
|
await this.cacheTweet(tweet);
|
|
8328
7642
|
}
|
|
8329
|
-
|
|
8330
|
-
`Populated ${tweetsToSave2.length} missing tweets from the cache.`
|
|
8331
|
-
);
|
|
7643
|
+
logger6.log(`Populated ${tweetsToSave2.length} missing tweets from the cache.`);
|
|
8332
7644
|
return;
|
|
8333
7645
|
}
|
|
8334
7646
|
}
|
|
@@ -8344,49 +7656,75 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8344
7656
|
const roomIds = /* @__PURE__ */ new Set();
|
|
8345
7657
|
for (const tweet of allTweets) {
|
|
8346
7658
|
tweetIdsToCheck.add(tweet.id);
|
|
8347
|
-
roomIds.add(
|
|
7659
|
+
roomIds.add(createUniqueUuid4(this.runtime, tweet.conversationId));
|
|
8348
7660
|
}
|
|
8349
|
-
const existingMemories = await this.runtime.
|
|
7661
|
+
const existingMemories = await this.runtime.getMemoriesByRoomIds({
|
|
7662
|
+
tableName: "messages",
|
|
8350
7663
|
roomIds: Array.from(roomIds)
|
|
8351
7664
|
});
|
|
8352
|
-
const existingMemoryIds = new Set(
|
|
8353
|
-
existingMemories.map((memory) => memory.id)
|
|
8354
|
-
);
|
|
7665
|
+
const existingMemoryIds = new Set(existingMemories.map((memory) => memory.id));
|
|
8355
7666
|
const tweetsToSave = allTweets.filter(
|
|
8356
|
-
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(
|
|
7667
|
+
(tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(createUniqueUuid4(this.runtime, tweet.id))
|
|
8357
7668
|
);
|
|
8358
|
-
|
|
7669
|
+
logger6.debug({
|
|
8359
7670
|
processingTweets: tweetsToSave.map((tweet) => tweet.id).join(",")
|
|
8360
7671
|
});
|
|
8361
7672
|
for (const tweet of tweetsToSave) {
|
|
8362
|
-
|
|
7673
|
+
logger6.log("Saving Tweet", tweet.id);
|
|
8363
7674
|
if (tweet.userId === this.profile.id) {
|
|
8364
7675
|
continue;
|
|
8365
7676
|
}
|
|
8366
|
-
const
|
|
8367
|
-
|
|
7677
|
+
const worldId = createUniqueUuid4(this.runtime, tweet.userId);
|
|
7678
|
+
await this.runtime.ensureWorldExists({
|
|
7679
|
+
id: worldId,
|
|
7680
|
+
name: `${tweet.username}'s Twitter`,
|
|
7681
|
+
agentId: this.runtime.agentId,
|
|
7682
|
+
serverId: tweet.userId,
|
|
7683
|
+
metadata: {
|
|
7684
|
+
ownership: { ownerId: tweet.userId },
|
|
7685
|
+
twitter: {
|
|
7686
|
+
username: tweet.username,
|
|
7687
|
+
id: tweet.userId
|
|
7688
|
+
}
|
|
7689
|
+
}
|
|
7690
|
+
});
|
|
7691
|
+
const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
|
|
7692
|
+
await this.runtime.ensureRoomExists({
|
|
7693
|
+
id: roomId,
|
|
7694
|
+
name: `${tweet.username}'s Thread`,
|
|
7695
|
+
source: "twitter",
|
|
7696
|
+
type: ChannelType4.FEED,
|
|
7697
|
+
channelId: tweet.conversationId,
|
|
7698
|
+
serverId: tweet.userId,
|
|
7699
|
+
worldId
|
|
7700
|
+
});
|
|
7701
|
+
const entityId = tweet.userId === this.profile.id ? this.runtime.agentId : createUniqueUuid4(this.runtime, tweet.userId);
|
|
8368
7702
|
await this.runtime.ensureConnection({
|
|
8369
7703
|
entityId,
|
|
8370
7704
|
roomId,
|
|
8371
7705
|
userName: tweet.username,
|
|
8372
7706
|
name: tweet.name,
|
|
8373
7707
|
source: "twitter",
|
|
8374
|
-
type:
|
|
7708
|
+
type: ChannelType4.FEED,
|
|
7709
|
+
worldId
|
|
8375
7710
|
});
|
|
8376
7711
|
const content = {
|
|
8377
7712
|
text: tweet.text,
|
|
8378
7713
|
url: tweet.permanentUrl,
|
|
8379
7714
|
source: "twitter",
|
|
8380
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
7715
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
|
|
8381
7716
|
};
|
|
8382
|
-
await this.runtime.
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
7717
|
+
await this.runtime.createMemory(
|
|
7718
|
+
{
|
|
7719
|
+
id: createUniqueUuid4(this.runtime, tweet.id),
|
|
7720
|
+
entityId,
|
|
7721
|
+
content,
|
|
7722
|
+
agentId: this.runtime.agentId,
|
|
7723
|
+
roomId,
|
|
7724
|
+
createdAt: tweet.timestamp * 1e3
|
|
7725
|
+
},
|
|
7726
|
+
"messages"
|
|
7727
|
+
);
|
|
8390
7728
|
await this.cacheTweet(tweet);
|
|
8391
7729
|
}
|
|
8392
7730
|
await this.cacheTimeline(timeline);
|
|
@@ -8400,15 +7738,16 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8400
7738
|
}
|
|
8401
7739
|
async saveRequestMessage(message, state) {
|
|
8402
7740
|
if (message.content.text) {
|
|
8403
|
-
const recentMessage = await this.runtime.
|
|
7741
|
+
const recentMessage = await this.runtime.getMemories({
|
|
7742
|
+
tableName: "messages",
|
|
8404
7743
|
roomId: message.roomId,
|
|
8405
7744
|
count: 1,
|
|
8406
7745
|
unique: false
|
|
8407
7746
|
});
|
|
8408
7747
|
if (recentMessage.length > 0 && recentMessage[0].content === message.content) {
|
|
8409
|
-
|
|
7748
|
+
logger6.debug("Message already saved", recentMessage[0].id);
|
|
8410
7749
|
} else {
|
|
8411
|
-
await this.runtime.
|
|
7750
|
+
await this.runtime.createMemory(message, "messages");
|
|
8412
7751
|
}
|
|
8413
7752
|
await this.runtime.evaluate(message, {
|
|
8414
7753
|
...state,
|
|
@@ -8417,7 +7756,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8417
7756
|
}
|
|
8418
7757
|
}
|
|
8419
7758
|
async loadLatestCheckedTweetId() {
|
|
8420
|
-
const latestCheckedTweetId = await this.runtime.
|
|
7759
|
+
const latestCheckedTweetId = await this.runtime.getCache(
|
|
8421
7760
|
`twitter/${this.profile.username}/latest_checked_tweet_id`
|
|
8422
7761
|
);
|
|
8423
7762
|
if (latestCheckedTweetId) {
|
|
@@ -8426,34 +7765,36 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8426
7765
|
}
|
|
8427
7766
|
async cacheLatestCheckedTweetId() {
|
|
8428
7767
|
if (this.lastCheckedTweetId) {
|
|
8429
|
-
await this.runtime.
|
|
7768
|
+
await this.runtime.setCache(
|
|
8430
7769
|
`twitter/${this.profile.username}/latest_checked_tweet_id`,
|
|
8431
7770
|
this.lastCheckedTweetId.toString()
|
|
8432
7771
|
);
|
|
8433
7772
|
}
|
|
8434
7773
|
}
|
|
8435
7774
|
async getCachedTimeline() {
|
|
8436
|
-
const cached = await this.runtime.
|
|
7775
|
+
const cached = await this.runtime.getCache(
|
|
7776
|
+
`twitter/${this.profile.username}/timeline`
|
|
7777
|
+
);
|
|
8437
7778
|
if (!cached) {
|
|
8438
7779
|
return void 0;
|
|
8439
7780
|
}
|
|
8440
7781
|
return cached;
|
|
8441
7782
|
}
|
|
8442
7783
|
async cacheTimeline(timeline) {
|
|
8443
|
-
await this.runtime.
|
|
7784
|
+
await this.runtime.setCache(`twitter/${this.profile.username}/timeline`, timeline);
|
|
8444
7785
|
}
|
|
8445
7786
|
async cacheMentions(mentions) {
|
|
8446
|
-
await this.runtime.
|
|
7787
|
+
await this.runtime.setCache(`twitter/${this.profile.username}/mentions`, mentions);
|
|
8447
7788
|
}
|
|
8448
7789
|
async getCachedCookies(username) {
|
|
8449
|
-
const cached = await this.runtime.
|
|
7790
|
+
const cached = await this.runtime.getCache(`twitter/${username}/cookies`);
|
|
8450
7791
|
if (!cached) {
|
|
8451
7792
|
return void 0;
|
|
8452
7793
|
}
|
|
8453
7794
|
return cached;
|
|
8454
7795
|
}
|
|
8455
7796
|
async cacheCookies(username, cookies) {
|
|
8456
|
-
await this.runtime.
|
|
7797
|
+
await this.runtime.setCache(`twitter/${username}/cookies`, cookies);
|
|
8457
7798
|
}
|
|
8458
7799
|
async fetchProfile(username) {
|
|
8459
7800
|
try {
|
|
@@ -8473,66 +7814,73 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8473
7814
|
throw error;
|
|
8474
7815
|
}
|
|
8475
7816
|
}
|
|
7817
|
+
/**
|
|
7818
|
+
* Fetches recent interactions (likes, retweets, quotes) for the authenticated user's tweets
|
|
7819
|
+
*/
|
|
7820
|
+
async fetchInteractions() {
|
|
7821
|
+
try {
|
|
7822
|
+
const username = this.profile.username;
|
|
7823
|
+
const mentionsResponse = await this.requestQueue.add(
|
|
7824
|
+
() => this.twitterClient.fetchSearchTweets(`@${username}`, 100, 1 /* Latest */)
|
|
7825
|
+
);
|
|
7826
|
+
return mentionsResponse.tweets.map((tweet) => ({
|
|
7827
|
+
id: tweet.id,
|
|
7828
|
+
type: tweet.isQuoted ? "quote" : tweet.retweetedStatus ? "retweet" : "like",
|
|
7829
|
+
userId: tweet.userId,
|
|
7830
|
+
username: tweet.username,
|
|
7831
|
+
name: tweet.name || tweet.username,
|
|
7832
|
+
targetTweetId: tweet.inReplyToStatusId || tweet.quotedStatusId,
|
|
7833
|
+
targetTweet: tweet.quotedStatus || tweet,
|
|
7834
|
+
quoteTweet: tweet.isQuoted ? tweet : void 0,
|
|
7835
|
+
retweetId: tweet.retweetedStatus?.id
|
|
7836
|
+
}));
|
|
7837
|
+
} catch (error) {
|
|
7838
|
+
logger6.error("Error fetching Twitter interactions:", error);
|
|
7839
|
+
return [];
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
8476
7842
|
};
|
|
7843
|
+
_ClientBase._twitterClients = {};
|
|
7844
|
+
var ClientBase = _ClientBase;
|
|
8477
7845
|
|
|
8478
7846
|
// src/constants.ts
|
|
8479
7847
|
var TWITTER_SERVICE_NAME = "twitter";
|
|
8480
7848
|
|
|
8481
7849
|
// src/interactions.ts
|
|
8482
7850
|
import {
|
|
8483
|
-
ChannelType as
|
|
8484
|
-
|
|
7851
|
+
ChannelType as ChannelType5,
|
|
7852
|
+
EventType as EventType2,
|
|
7853
|
+
ModelType as ModelType4,
|
|
8485
7854
|
composePrompt as composePrompt2,
|
|
8486
|
-
createUniqueUuid as
|
|
8487
|
-
logger as
|
|
8488
|
-
parseJSONObjectFromText
|
|
7855
|
+
createUniqueUuid as createUniqueUuid5,
|
|
7856
|
+
logger as logger7
|
|
8489
7857
|
} from "@elizaos/core";
|
|
8490
|
-
var twitterMessageHandlerTemplate = `# Task: Generate dialog and actions for {{agentName}}.
|
|
8491
|
-
{{providers}}
|
|
8492
|
-
Here is the current post text again. Remember to include an action if the current post text includes a prompt that asks for one of the available actions mentioned above (does not need to be exact)
|
|
8493
|
-
{{currentPost}}
|
|
8494
|
-
{{imageDescriptions}}
|
|
8495
|
-
|
|
8496
|
-
# Instructions: Write the next message for {{agentName}}. Include the appropriate action from the list: {{actionNames}}
|
|
8497
|
-
Response format should be formatted in a valid JSON block like this:
|
|
8498
|
-
\`\`\`json
|
|
8499
|
-
{ "thought": "<string>", "name": "{{agentName}}", "text": "<string>", "action": "<string>" }
|
|
8500
|
-
\`\`\`
|
|
8501
7858
|
|
|
8502
|
-
|
|
8503
|
-
var
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
For other users:
|
|
8508
|
-
- {{agentName}} should RESPOND to messages directed at them
|
|
8509
|
-
- {{agentName}} should RESPOND to conversations relevant to their background
|
|
8510
|
-
- {{agentName}} should IGNORE irrelevant messages
|
|
8511
|
-
- {{agentName}} should IGNORE very short messages unless directly addressed
|
|
8512
|
-
- {{agentName}} should STOP if asked to stop
|
|
8513
|
-
- {{agentName}} should STOP if conversation is concluded
|
|
8514
|
-
- {{agentName}} is in a room with other users and wants to be conversational, but not annoying.
|
|
8515
|
-
|
|
8516
|
-
IMPORTANT:
|
|
8517
|
-
- {{agentName}} (aka @{{twitterUserName}}) is particularly sensitive about being annoying, so if there is any doubt, it is better to IGNORE than to RESPOND.
|
|
8518
|
-
- For users not in the priority list, {{agentName}} (@{{twitterUserName}}) should err on the side of IGNORE rather than RESPOND if in doubt.
|
|
8519
|
-
|
|
8520
|
-
Recent Posts:
|
|
8521
|
-
{{recentPosts}}
|
|
8522
|
-
|
|
8523
|
-
Current Post:
|
|
8524
|
-
{{currentPost}}
|
|
8525
|
-
|
|
8526
|
-
Thread of Tweets You Are Replying To:
|
|
8527
|
-
{{formattedConversation}}
|
|
7859
|
+
// src/types.ts
|
|
7860
|
+
var ServiceType2 = {
|
|
7861
|
+
TWITTER: "twitter"
|
|
7862
|
+
};
|
|
8528
7863
|
|
|
8529
|
-
|
|
8530
|
-
|
|
7864
|
+
// src/interactions.ts
|
|
7865
|
+
var convertToCoreTweet = (tweet) => ({
|
|
7866
|
+
id: tweet.id,
|
|
7867
|
+
text: tweet.text,
|
|
7868
|
+
conversationId: tweet.conversationId,
|
|
7869
|
+
timestamp: tweet.timestamp,
|
|
7870
|
+
userId: tweet.userId,
|
|
7871
|
+
username: tweet.username,
|
|
7872
|
+
name: tweet.name,
|
|
7873
|
+
inReplyToStatusId: tweet.inReplyToStatusId,
|
|
7874
|
+
permanentUrl: tweet.permanentUrl,
|
|
7875
|
+
photos: tweet.photos,
|
|
7876
|
+
hashtags: tweet.hashtags,
|
|
7877
|
+
mentions: tweet.mentions.map((mention) => mention.username),
|
|
7878
|
+
urls: tweet.urls,
|
|
7879
|
+
videos: tweet.videos,
|
|
7880
|
+
thread: tweet.thread
|
|
7881
|
+
});
|
|
7882
|
+
var convertToCoreTweets = (tweets) => tweets.map(convertToCoreTweet);
|
|
8531
7883
|
var TwitterInteractionClient = class {
|
|
8532
|
-
client;
|
|
8533
|
-
runtime;
|
|
8534
|
-
isDryRun;
|
|
8535
|
-
state;
|
|
8536
7884
|
/**
|
|
8537
7885
|
* Constructor for setting up a new instance with the provided client, runtime, and state.
|
|
8538
7886
|
* @param {ClientBase} client - The client being used for communication.
|
|
@@ -8551,9 +7899,7 @@ var TwitterInteractionClient = class {
|
|
|
8551
7899
|
*/
|
|
8552
7900
|
async start() {
|
|
8553
7901
|
const handleTwitterInteractionsLoop = () => {
|
|
8554
|
-
const interactionInterval = (this.state?.TWITTER_POLL_INTERVAL || this.runtime.getSetting(
|
|
8555
|
-
"TWITTER_POLL_INTERVAL"
|
|
8556
|
-
) || 120) * 1e3;
|
|
7902
|
+
const interactionInterval = (this.state?.TWITTER_POLL_INTERVAL || this.runtime.getSetting("TWITTER_POLL_INTERVAL") || 120) * 1e3;
|
|
8557
7903
|
this.handleTwitterInteractions();
|
|
8558
7904
|
setTimeout(handleTwitterInteractionsLoop, interactionInterval);
|
|
8559
7905
|
};
|
|
@@ -8563,66 +7909,260 @@ var TwitterInteractionClient = class {
|
|
|
8563
7909
|
* Asynchronously handles Twitter interactions by checking for mentions, processing tweets, and updating the last checked tweet ID.
|
|
8564
7910
|
*/
|
|
8565
7911
|
async handleTwitterInteractions() {
|
|
8566
|
-
|
|
7912
|
+
logger7.log("Checking Twitter interactions");
|
|
8567
7913
|
const twitterUsername = this.client.profile?.username;
|
|
8568
7914
|
try {
|
|
8569
|
-
const mentionCandidates = (await this.client.fetchSearchTweets(
|
|
8570
|
-
|
|
8571
|
-
20,
|
|
8572
|
-
1 /* Latest */
|
|
8573
|
-
)).tweets;
|
|
8574
|
-
logger6.log(
|
|
8575
|
-
"Completed checking mentioned tweets:",
|
|
8576
|
-
mentionCandidates.length
|
|
8577
|
-
);
|
|
7915
|
+
const mentionCandidates = (await this.client.fetchSearchTweets(`@${twitterUsername}`, 20, 1 /* Latest */)).tweets;
|
|
7916
|
+
logger7.log("Completed checking mentioned tweets:", mentionCandidates.length);
|
|
8578
7917
|
let uniqueTweetCandidates = [...mentionCandidates];
|
|
8579
7918
|
uniqueTweetCandidates = uniqueTweetCandidates.sort((a, b) => a.id.localeCompare(b.id)).filter((tweet) => tweet.userId !== this.client.profile.id);
|
|
8580
7919
|
for (const tweet of uniqueTweetCandidates) {
|
|
8581
7920
|
if (!this.client.lastCheckedTweetId || BigInt(tweet.id) > this.client.lastCheckedTweetId) {
|
|
8582
|
-
const tweetId =
|
|
8583
|
-
const existingResponse = await this.runtime.
|
|
7921
|
+
const tweetId = createUniqueUuid5(this.runtime, tweet.id);
|
|
7922
|
+
const existingResponse = await this.runtime.getMemoryById(tweetId);
|
|
8584
7923
|
if (existingResponse) {
|
|
8585
|
-
|
|
7924
|
+
logger7.log(`Already responded to tweet ${tweet.id}, skipping`);
|
|
8586
7925
|
continue;
|
|
8587
7926
|
}
|
|
8588
|
-
|
|
8589
|
-
const
|
|
8590
|
-
const entityId = createUniqueUuid4(
|
|
7927
|
+
logger7.log("New Tweet found", tweet.permanentUrl);
|
|
7928
|
+
const entityId = createUniqueUuid5(
|
|
8591
7929
|
this.runtime,
|
|
8592
7930
|
tweet.userId === this.client.profile.id ? this.runtime.agentId : tweet.userId
|
|
8593
7931
|
);
|
|
7932
|
+
const worldId = createUniqueUuid5(this.runtime, tweet.userId);
|
|
7933
|
+
const roomId = createUniqueUuid5(this.runtime, tweet.conversationId);
|
|
7934
|
+
await this.runtime.ensureWorldExists({
|
|
7935
|
+
id: worldId,
|
|
7936
|
+
name: `${tweet.name}'s Twitter`,
|
|
7937
|
+
agentId: this.runtime.agentId,
|
|
7938
|
+
serverId: tweet.userId,
|
|
7939
|
+
metadata: {
|
|
7940
|
+
ownership: { ownerId: tweet.userId },
|
|
7941
|
+
twitter: {
|
|
7942
|
+
username: tweet.username,
|
|
7943
|
+
id: tweet.userId,
|
|
7944
|
+
name: tweet.name
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
});
|
|
8594
7948
|
await this.runtime.ensureConnection({
|
|
8595
7949
|
entityId,
|
|
8596
7950
|
roomId,
|
|
8597
7951
|
userName: tweet.username,
|
|
8598
7952
|
name: tweet.name,
|
|
8599
7953
|
source: "twitter",
|
|
8600
|
-
type:
|
|
7954
|
+
type: ChannelType5.GROUP,
|
|
7955
|
+
channelId: tweet.conversationId,
|
|
7956
|
+
serverId: tweet.userId,
|
|
7957
|
+
worldId
|
|
7958
|
+
});
|
|
7959
|
+
await this.runtime.ensureRoomExists({
|
|
7960
|
+
id: roomId,
|
|
7961
|
+
name: `Conversation with ${tweet.name}`,
|
|
7962
|
+
source: "twitter",
|
|
7963
|
+
type: ChannelType5.GROUP,
|
|
7964
|
+
channelId: tweet.conversationId,
|
|
7965
|
+
serverId: tweet.userId,
|
|
7966
|
+
worldId
|
|
8601
7967
|
});
|
|
8602
|
-
const
|
|
8603
|
-
|
|
7968
|
+
const memory = {
|
|
7969
|
+
id: tweetId,
|
|
7970
|
+
agentId: this.runtime.agentId,
|
|
8604
7971
|
content: {
|
|
8605
7972
|
text: tweet.text,
|
|
7973
|
+
url: tweet.permanentUrl,
|
|
8606
7974
|
imageUrls: tweet.photos?.map((photo) => photo.url) || [],
|
|
8607
|
-
tweet,
|
|
8608
|
-
source: "twitter"
|
|
7975
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid5(this.runtime, tweet.inReplyToStatusId) : void 0,
|
|
7976
|
+
source: "twitter",
|
|
7977
|
+
channelType: ChannelType5.GROUP
|
|
8609
7978
|
},
|
|
8610
|
-
agentId: this.runtime.agentId,
|
|
8611
7979
|
entityId,
|
|
8612
|
-
roomId
|
|
7980
|
+
roomId,
|
|
7981
|
+
createdAt: tweet.timestamp * 1e3
|
|
8613
7982
|
};
|
|
7983
|
+
await this.runtime.createMemory(memory, "messages");
|
|
7984
|
+
if (tweet.text.includes(`@${twitterUsername}`)) {
|
|
7985
|
+
const messagePayload = {
|
|
7986
|
+
runtime: this.runtime,
|
|
7987
|
+
message: {
|
|
7988
|
+
...memory,
|
|
7989
|
+
source: "twitter"
|
|
7990
|
+
},
|
|
7991
|
+
source: "twitter",
|
|
7992
|
+
callback: async (response) => {
|
|
7993
|
+
logger7.info("Received message response:", response);
|
|
7994
|
+
return [];
|
|
7995
|
+
}
|
|
7996
|
+
};
|
|
7997
|
+
this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, messagePayload);
|
|
7998
|
+
const mentionPayload = {
|
|
7999
|
+
runtime: this.runtime,
|
|
8000
|
+
message: {
|
|
8001
|
+
...memory,
|
|
8002
|
+
source: "twitter"
|
|
8003
|
+
},
|
|
8004
|
+
tweet: convertToCoreTweet(tweet),
|
|
8005
|
+
user: {
|
|
8006
|
+
id: tweet.userId,
|
|
8007
|
+
username: tweet.username,
|
|
8008
|
+
name: tweet.name
|
|
8009
|
+
},
|
|
8010
|
+
source: "twitter",
|
|
8011
|
+
callback: async (response) => {
|
|
8012
|
+
logger7.info("Received mention response:", response);
|
|
8013
|
+
return [];
|
|
8014
|
+
}
|
|
8015
|
+
};
|
|
8016
|
+
this.runtime.emitEvent("TWITTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
|
|
8017
|
+
}
|
|
8018
|
+
if (tweet.thread.length > 1) {
|
|
8019
|
+
const threadPayload = {
|
|
8020
|
+
runtime: this.runtime,
|
|
8021
|
+
tweets: convertToCoreTweets(tweet.thread),
|
|
8022
|
+
user: {
|
|
8023
|
+
id: tweet.userId,
|
|
8024
|
+
username: tweet.username,
|
|
8025
|
+
name: tweet.name
|
|
8026
|
+
},
|
|
8027
|
+
source: "twitter"
|
|
8028
|
+
};
|
|
8029
|
+
if (tweet.thread[tweet.thread.length - 1].id === tweet.id) {
|
|
8030
|
+
this.runtime.emitEvent("TWITTER_THREAD_UPDATED" /* THREAD_UPDATED */, {
|
|
8031
|
+
...threadPayload,
|
|
8032
|
+
newTweet: convertToCoreTweet(tweet)
|
|
8033
|
+
});
|
|
8034
|
+
} else if (tweet.thread[0].id === tweet.id) {
|
|
8035
|
+
this.runtime.emitEvent("TWITTER_THREAD_CREATED" /* THREAD_CREATED */, threadPayload);
|
|
8036
|
+
}
|
|
8037
|
+
}
|
|
8614
8038
|
await this.handleTweet({
|
|
8615
8039
|
tweet,
|
|
8616
|
-
message,
|
|
8617
|
-
thread
|
|
8040
|
+
message: memory,
|
|
8041
|
+
thread: tweet.thread
|
|
8618
8042
|
});
|
|
8619
8043
|
this.client.lastCheckedTweetId = BigInt(tweet.id);
|
|
8620
8044
|
}
|
|
8621
8045
|
}
|
|
8046
|
+
const interactions = await this.client.fetchInteractions();
|
|
8047
|
+
const handleInteraction = async (interaction) => {
|
|
8048
|
+
if (interaction?.targetTweet?.conversationId) {
|
|
8049
|
+
const memory = this.createMemoryObject(
|
|
8050
|
+
interaction.type,
|
|
8051
|
+
`${interaction.id}-${interaction.type}`,
|
|
8052
|
+
interaction.userId,
|
|
8053
|
+
interaction.targetTweet.conversationId
|
|
8054
|
+
);
|
|
8055
|
+
await this.runtime.createMemory(memory, "messages");
|
|
8056
|
+
const reactionMessage = {
|
|
8057
|
+
id: createUniqueUuid5(this.runtime, interaction.targetTweetId),
|
|
8058
|
+
content: {
|
|
8059
|
+
text: interaction.targetTweet.text,
|
|
8060
|
+
source: "twitter"
|
|
8061
|
+
},
|
|
8062
|
+
entityId: createUniqueUuid5(this.runtime, interaction.targetTweet.userId),
|
|
8063
|
+
roomId: createUniqueUuid5(this.runtime, interaction.targetTweet.conversationId),
|
|
8064
|
+
agentId: this.runtime.agentId
|
|
8065
|
+
};
|
|
8066
|
+
const basePayload = {
|
|
8067
|
+
runtime: this.runtime,
|
|
8068
|
+
user: {
|
|
8069
|
+
id: interaction.userId,
|
|
8070
|
+
username: interaction.username,
|
|
8071
|
+
name: interaction.name
|
|
8072
|
+
},
|
|
8073
|
+
source: "twitter"
|
|
8074
|
+
};
|
|
8075
|
+
switch (interaction.type) {
|
|
8076
|
+
case "like": {
|
|
8077
|
+
const likePayload = {
|
|
8078
|
+
...basePayload,
|
|
8079
|
+
tweet: interaction.targetTweet
|
|
8080
|
+
};
|
|
8081
|
+
this.runtime.emitEvent("TWITTER_LIKE_RECEIVED" /* LIKE_RECEIVED */, likePayload);
|
|
8082
|
+
this.runtime.emitEvent(EventType2.REACTION_RECEIVED, {
|
|
8083
|
+
...basePayload,
|
|
8084
|
+
reaction: {
|
|
8085
|
+
type: "like",
|
|
8086
|
+
entityId: createUniqueUuid5(this.runtime, interaction.userId)
|
|
8087
|
+
},
|
|
8088
|
+
message: reactionMessage,
|
|
8089
|
+
callback: async () => {
|
|
8090
|
+
return [];
|
|
8091
|
+
}
|
|
8092
|
+
});
|
|
8093
|
+
break;
|
|
8094
|
+
}
|
|
8095
|
+
case "retweet": {
|
|
8096
|
+
const retweetPayload = {
|
|
8097
|
+
...basePayload,
|
|
8098
|
+
tweet: interaction.targetTweet,
|
|
8099
|
+
retweetId: interaction.retweetId
|
|
8100
|
+
};
|
|
8101
|
+
this.runtime.emitEvent("TWITTER_RETWEET_RECEIVED" /* RETWEET_RECEIVED */, retweetPayload);
|
|
8102
|
+
this.runtime.emitEvent(EventType2.REACTION_RECEIVED, {
|
|
8103
|
+
...basePayload,
|
|
8104
|
+
reaction: {
|
|
8105
|
+
type: "retweet",
|
|
8106
|
+
entityId: createUniqueUuid5(this.runtime, interaction.userId)
|
|
8107
|
+
},
|
|
8108
|
+
message: reactionMessage,
|
|
8109
|
+
callback: async () => {
|
|
8110
|
+
return [];
|
|
8111
|
+
}
|
|
8112
|
+
});
|
|
8113
|
+
break;
|
|
8114
|
+
}
|
|
8115
|
+
case "quote": {
|
|
8116
|
+
const quotePayload = {
|
|
8117
|
+
...basePayload,
|
|
8118
|
+
message: reactionMessage,
|
|
8119
|
+
quotedTweet: interaction.targetTweet,
|
|
8120
|
+
quoteTweet: interaction.quoteTweet || interaction.targetTweet,
|
|
8121
|
+
callback: async () => [],
|
|
8122
|
+
reaction: {
|
|
8123
|
+
type: "quote",
|
|
8124
|
+
entityId: createUniqueUuid5(this.runtime, interaction.userId)
|
|
8125
|
+
}
|
|
8126
|
+
};
|
|
8127
|
+
this.runtime.emitEvent("TWITTER_QUOTE_RECEIVED" /* QUOTE_RECEIVED */, quotePayload);
|
|
8128
|
+
this.runtime.emitEvent(EventType2.REACTION_RECEIVED, {
|
|
8129
|
+
...basePayload,
|
|
8130
|
+
reaction: {
|
|
8131
|
+
type: "quote",
|
|
8132
|
+
entityId: createUniqueUuid5(this.runtime, interaction.userId)
|
|
8133
|
+
},
|
|
8134
|
+
message: reactionMessage,
|
|
8135
|
+
callback: async () => {
|
|
8136
|
+
return [];
|
|
8137
|
+
}
|
|
8138
|
+
});
|
|
8139
|
+
break;
|
|
8140
|
+
}
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
};
|
|
8144
|
+
const processInteractions = async (interactions2) => {
|
|
8145
|
+
for (const interaction of interactions2) {
|
|
8146
|
+
if (interaction?.targetTweet?.conversationId) {
|
|
8147
|
+
await handleInteraction(interaction);
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8150
|
+
};
|
|
8151
|
+
const processFollowerChange = async (change, profileId) => {
|
|
8152
|
+
if (change?.type && change?.userId && profileId) {
|
|
8153
|
+
const followerMemory = this.createMemoryObject(
|
|
8154
|
+
change.type,
|
|
8155
|
+
`${change.type}-${change.userId}`,
|
|
8156
|
+
change.userId,
|
|
8157
|
+
profileId
|
|
8158
|
+
);
|
|
8159
|
+
await this.runtime.createMemory(followerMemory, "follower-changes");
|
|
8160
|
+
}
|
|
8161
|
+
};
|
|
8622
8162
|
await this.client.cacheLatestCheckedTweetId();
|
|
8623
|
-
|
|
8163
|
+
logger7.log("Finished checking Twitter interactions");
|
|
8624
8164
|
} catch (error) {
|
|
8625
|
-
|
|
8165
|
+
logger7.error("Error handling Twitter interactions:", error);
|
|
8626
8166
|
}
|
|
8627
8167
|
}
|
|
8628
8168
|
/**
|
|
@@ -8643,10 +8183,10 @@ var TwitterInteractionClient = class {
|
|
|
8643
8183
|
thread
|
|
8644
8184
|
}) {
|
|
8645
8185
|
if (!message.content.text) {
|
|
8646
|
-
|
|
8186
|
+
logger7.log("Skipping Tweet with no text", tweet.id);
|
|
8647
8187
|
return { text: "", actions: ["IGNORE"] };
|
|
8648
8188
|
}
|
|
8649
|
-
|
|
8189
|
+
logger7.log("Processing Tweet: ", tweet.id);
|
|
8650
8190
|
const formatTweet = (tweet2) => {
|
|
8651
8191
|
return ` ID: ${tweet2.id}
|
|
8652
8192
|
From: ${tweet2.name} (@${tweet2.username})
|
|
@@ -8654,9 +8194,7 @@ var TwitterInteractionClient = class {
|
|
|
8654
8194
|
};
|
|
8655
8195
|
const currentPost = formatTweet(tweet);
|
|
8656
8196
|
const formattedConversation = thread.map(
|
|
8657
|
-
(tweet2) => `@${tweet2.username} (${new Date(
|
|
8658
|
-
tweet2.timestamp * 1e3
|
|
8659
|
-
).toLocaleString("en-US", {
|
|
8197
|
+
(tweet2) => `@${tweet2.username} (${new Date(tweet2.timestamp * 1e3).toLocaleString("en-US", {
|
|
8660
8198
|
hour: "2-digit",
|
|
8661
8199
|
minute: "2-digit",
|
|
8662
8200
|
month: "short",
|
|
@@ -8667,148 +8205,117 @@ var TwitterInteractionClient = class {
|
|
|
8667
8205
|
const imageDescriptionsArray = [];
|
|
8668
8206
|
try {
|
|
8669
8207
|
for (const photo of tweet.photos) {
|
|
8670
|
-
const description = await this.runtime.useModel(
|
|
8671
|
-
ModelTypes4.IMAGE_DESCRIPTION,
|
|
8672
|
-
photo.url
|
|
8673
|
-
);
|
|
8208
|
+
const description = await this.runtime.useModel(ModelType4.IMAGE_DESCRIPTION, photo.url);
|
|
8674
8209
|
imageDescriptionsArray.push(description);
|
|
8675
8210
|
}
|
|
8676
8211
|
} catch (error) {
|
|
8677
|
-
|
|
8212
|
+
logger7.error("Error Occured during describing image: ", error);
|
|
8678
8213
|
}
|
|
8679
|
-
|
|
8214
|
+
const state = await this.runtime.composeState(message);
|
|
8680
8215
|
state.values = {
|
|
8681
8216
|
...state.values,
|
|
8682
8217
|
twitterUserName: this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME"),
|
|
8683
8218
|
currentPost,
|
|
8684
|
-
// TODO: move to recentMessages provider
|
|
8685
8219
|
formattedConversation
|
|
8686
8220
|
};
|
|
8687
|
-
const tweetId =
|
|
8688
|
-
const tweetExists = await this.runtime.
|
|
8221
|
+
const tweetId = createUniqueUuid5(this.runtime, tweet.id);
|
|
8222
|
+
const tweetExists = await this.runtime.getMemoryById(tweetId);
|
|
8689
8223
|
if (!tweetExists) {
|
|
8690
|
-
|
|
8691
|
-
const entityId =
|
|
8692
|
-
const roomId =
|
|
8224
|
+
logger7.log("tweet does not exist, saving");
|
|
8225
|
+
const entityId = createUniqueUuid5(this.runtime, tweet.userId);
|
|
8226
|
+
const roomId = createUniqueUuid5(this.runtime, tweet.conversationId);
|
|
8693
8227
|
await this.runtime.ensureConnection({
|
|
8694
8228
|
entityId,
|
|
8695
8229
|
roomId,
|
|
8696
8230
|
userName: tweet.username,
|
|
8697
8231
|
name: tweet.name,
|
|
8698
8232
|
source: "twitter",
|
|
8699
|
-
type:
|
|
8233
|
+
type: ChannelType5.GROUP
|
|
8700
8234
|
});
|
|
8701
|
-
|
|
8235
|
+
await this.runtime.ensureRoomExists({
|
|
8236
|
+
id: roomId,
|
|
8237
|
+
name: `Conversation with ${tweet.name}`,
|
|
8238
|
+
source: "twitter",
|
|
8239
|
+
type: ChannelType5.GROUP,
|
|
8240
|
+
channelId: tweet.conversationId,
|
|
8241
|
+
worldId: createUniqueUuid5(this.runtime, tweet.userId)
|
|
8242
|
+
});
|
|
8243
|
+
const memory = {
|
|
8702
8244
|
id: tweetId,
|
|
8703
8245
|
agentId: this.runtime.agentId,
|
|
8704
8246
|
content: {
|
|
8705
8247
|
text: tweet.text,
|
|
8706
8248
|
url: tweet.permanentUrl,
|
|
8707
8249
|
imageUrls: tweet.photos?.map((photo) => photo.url) || [],
|
|
8708
|
-
inReplyTo: tweet.inReplyToStatusId ?
|
|
8250
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid5(this.runtime, tweet.inReplyToStatusId) : void 0,
|
|
8251
|
+
source: "twitter",
|
|
8252
|
+
channelType: ChannelType5.GROUP
|
|
8709
8253
|
},
|
|
8710
8254
|
entityId,
|
|
8711
8255
|
roomId,
|
|
8712
8256
|
createdAt: tweet.timestamp * 1e3
|
|
8713
8257
|
};
|
|
8714
|
-
this.client.saveRequestMessage(
|
|
8258
|
+
this.client.saveRequestMessage(memory, state);
|
|
8715
8259
|
}
|
|
8716
8260
|
const shouldRespondPrompt = composePrompt2({
|
|
8717
8261
|
state,
|
|
8718
|
-
template: this.runtime.character.templates?.
|
|
8262
|
+
template: this.runtime.character.templates?.shouldRespondTemplate || ""
|
|
8719
8263
|
});
|
|
8720
|
-
const
|
|
8264
|
+
const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
|
|
8721
8265
|
prompt: shouldRespondPrompt
|
|
8722
8266
|
});
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8267
|
+
const responseActions = (response.match(/(?:RESPOND|IGNORE|STOP)/g) || ["IGNORE"])[0];
|
|
8268
|
+
if (responseActions !== "RESPOND") {
|
|
8269
|
+
logger7.log(`Not responding to tweet based on shouldRespond decision: ${responseActions}`);
|
|
8270
|
+
return { text: "", actions: [responseActions] };
|
|
8726
8271
|
}
|
|
8727
|
-
const
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
actionNames: Array.isArray(state.actionNames) ? state.actionNames.join(", ") : state.actionNames || "",
|
|
8734
|
-
actions: Array.isArray(state.actions) ? state.actions.join("\n") : state.actions || "",
|
|
8735
|
-
// Ensure character examples are included
|
|
8736
|
-
characterPostExamples: this.runtime.character.messageExamples ? this.runtime.character.messageExamples.map(
|
|
8737
|
-
(example) => example.map(
|
|
8738
|
-
(msg) => `${msg.name}: ${msg.content.text}${msg.content.actions ? ` (Actions: ${msg.content.actions.join(", ")})` : ""}`
|
|
8739
|
-
).join("\n")
|
|
8740
|
-
).join("\n\n") : ""
|
|
8272
|
+
const callback = async (response2, tweetId2) => {
|
|
8273
|
+
try {
|
|
8274
|
+
const tweetToReplyTo = tweetId2 || tweet.id;
|
|
8275
|
+
if (this.isDryRun) {
|
|
8276
|
+
logger7.info(`[DRY RUN] Would have replied to ${tweet.username} with: ${response2.text}`);
|
|
8277
|
+
return [];
|
|
8741
8278
|
}
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
const removeQuotes = (str) => str.replace(/^['"](.*)['"]$/, "$1");
|
|
8750
|
-
const replyToId = createUniqueUuid4(this.runtime, tweet.id);
|
|
8751
|
-
response.inReplyTo = replyToId;
|
|
8752
|
-
response.text = removeQuotes(response.text);
|
|
8753
|
-
if (response.text) {
|
|
8754
|
-
if (this.isDryRun) {
|
|
8755
|
-
logger6.info(
|
|
8756
|
-
`Dry run: Selected Post: ${tweet.id} - ${tweet.username}: ${tweet.text}
|
|
8757
|
-
Agent's Output:
|
|
8758
|
-
${response.text}`
|
|
8279
|
+
logger7.info(`Replying to tweet ${tweetToReplyTo}`);
|
|
8280
|
+
const replyTweetResult = await this.client.requestQueue.add(
|
|
8281
|
+
() => this.client.twitterClient.post("statuses/update", {
|
|
8282
|
+
status: response2.text.substring(0, 280),
|
|
8283
|
+
in_reply_to_status_id: tweetToReplyTo,
|
|
8284
|
+
auto_populate_reply_metadata: true
|
|
8285
|
+
})
|
|
8759
8286
|
);
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
const callback = async (response2, tweetId2) => {
|
|
8763
|
-
const memories = await sendTweet(
|
|
8764
|
-
this.client,
|
|
8765
|
-
response2,
|
|
8766
|
-
message.roomId,
|
|
8767
|
-
this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME"),
|
|
8768
|
-
tweetId2 || tweet.id
|
|
8769
|
-
);
|
|
8770
|
-
return memories;
|
|
8771
|
-
};
|
|
8772
|
-
const responseMessages = [
|
|
8773
|
-
{
|
|
8774
|
-
id: createUniqueUuid4(this.runtime, tweet.id),
|
|
8775
|
-
entityId: this.runtime.agentId,
|
|
8776
|
-
agentId: this.runtime.agentId,
|
|
8777
|
-
content: response,
|
|
8778
|
-
roomId: message.roomId,
|
|
8779
|
-
createdAt: Date.now()
|
|
8780
|
-
}
|
|
8781
|
-
];
|
|
8782
|
-
state = await this.runtime.composeState(message, ["RECENT_MESSAGES"]);
|
|
8783
|
-
for (const responseMessage of responseMessages) {
|
|
8784
|
-
await this.runtime.getMemoryManager("messages").createMemory(responseMessage);
|
|
8785
|
-
}
|
|
8786
|
-
const responseTweetId = responseMessages[responseMessages.length - 1]?.content?.tweetId;
|
|
8787
|
-
await this.runtime.processActions(
|
|
8788
|
-
message,
|
|
8789
|
-
responseMessages,
|
|
8790
|
-
state,
|
|
8791
|
-
(response2) => {
|
|
8792
|
-
return callback(response2, responseTweetId);
|
|
8793
|
-
}
|
|
8794
|
-
);
|
|
8795
|
-
const responseInfo = `Context:
|
|
8796
|
-
|
|
8797
|
-
${prompt}
|
|
8798
|
-
|
|
8799
|
-
Selected Post: ${tweet.id} - ${tweet.username}: ${tweet.text}
|
|
8800
|
-
Agent's Output:
|
|
8801
|
-
${response.text}`;
|
|
8802
|
-
await this.runtime.getDatabaseAdapter().setCache(
|
|
8803
|
-
`twitter/tweet_generation_${tweet.id}.txt`,
|
|
8804
|
-
responseInfo
|
|
8805
|
-
);
|
|
8806
|
-
await wait();
|
|
8807
|
-
} catch (error) {
|
|
8808
|
-
logger6.error(`Error sending response tweet: ${error}`);
|
|
8287
|
+
if (!replyTweetResult) {
|
|
8288
|
+
throw new Error("Failed to create tweet response");
|
|
8809
8289
|
}
|
|
8290
|
+
const responseId = createUniqueUuid5(
|
|
8291
|
+
this.runtime,
|
|
8292
|
+
replyTweetResult.id_str || replyTweetResult.rest_id || replyTweetResult.legacy.id_str || replyTweetResult.id
|
|
8293
|
+
);
|
|
8294
|
+
const responseMemory = {
|
|
8295
|
+
id: responseId,
|
|
8296
|
+
entityId: this.runtime.agentId,
|
|
8297
|
+
agentId: this.runtime.agentId,
|
|
8298
|
+
roomId: message.roomId,
|
|
8299
|
+
content: {
|
|
8300
|
+
...response2,
|
|
8301
|
+
inReplyTo: message.id
|
|
8302
|
+
},
|
|
8303
|
+
createdAt: Date.now()
|
|
8304
|
+
};
|
|
8305
|
+
await this.runtime.createMemory(responseMemory, "messages");
|
|
8306
|
+
return [responseMemory];
|
|
8307
|
+
} catch (error) {
|
|
8308
|
+
logger7.error("Error replying to tweet:", error);
|
|
8309
|
+
return [];
|
|
8810
8310
|
}
|
|
8811
|
-
}
|
|
8311
|
+
};
|
|
8312
|
+
this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
|
|
8313
|
+
runtime: this.runtime,
|
|
8314
|
+
message,
|
|
8315
|
+
callback,
|
|
8316
|
+
source: "twitter"
|
|
8317
|
+
});
|
|
8318
|
+
return { text: "", actions: ["RESPOND"] };
|
|
8812
8319
|
}
|
|
8813
8320
|
/**
|
|
8814
8321
|
* Build a conversation thread based on a given tweet.
|
|
@@ -8821,112 +8328,107 @@ ${response.text}`;
|
|
|
8821
8328
|
const thread = [];
|
|
8822
8329
|
const visited = /* @__PURE__ */ new Set();
|
|
8823
8330
|
async function processThread(currentTweet, depth = 0) {
|
|
8824
|
-
|
|
8331
|
+
logger7.log("Processing tweet:", {
|
|
8825
8332
|
id: currentTweet.id,
|
|
8826
8333
|
inReplyToStatusId: currentTweet.inReplyToStatusId,
|
|
8827
8334
|
depth
|
|
8828
8335
|
});
|
|
8829
8336
|
if (!currentTweet) {
|
|
8830
|
-
|
|
8337
|
+
logger7.log("No current tweet found for thread building");
|
|
8831
8338
|
return;
|
|
8832
8339
|
}
|
|
8833
8340
|
if (depth >= maxReplies) {
|
|
8834
|
-
|
|
8341
|
+
logger7.log("Reached maximum reply depth", depth);
|
|
8835
8342
|
return;
|
|
8836
8343
|
}
|
|
8837
|
-
const memory = await this.runtime.
|
|
8344
|
+
const memory = await this.runtime.getMemoryById(
|
|
8345
|
+
createUniqueUuid5(this.runtime, currentTweet.id)
|
|
8346
|
+
);
|
|
8838
8347
|
if (!memory) {
|
|
8839
|
-
const roomId =
|
|
8840
|
-
const entityId =
|
|
8348
|
+
const roomId = createUniqueUuid5(this.runtime, tweet.conversationId);
|
|
8349
|
+
const entityId = createUniqueUuid5(this.runtime, currentTweet.userId);
|
|
8841
8350
|
await this.runtime.ensureConnection({
|
|
8842
8351
|
entityId,
|
|
8843
8352
|
roomId,
|
|
8844
8353
|
userName: currentTweet.username,
|
|
8845
8354
|
name: currentTweet.name,
|
|
8846
8355
|
source: "twitter",
|
|
8847
|
-
type:
|
|
8356
|
+
type: ChannelType5.GROUP
|
|
8848
8357
|
});
|
|
8849
|
-
this.runtime.
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8358
|
+
this.runtime.createMemory(
|
|
8359
|
+
{
|
|
8360
|
+
id: createUniqueUuid5(this.runtime, currentTweet.id),
|
|
8361
|
+
agentId: this.runtime.agentId,
|
|
8362
|
+
content: {
|
|
8363
|
+
text: currentTweet.text,
|
|
8364
|
+
source: "twitter",
|
|
8365
|
+
url: currentTweet.permanentUrl,
|
|
8366
|
+
imageUrls: currentTweet.photos?.map((photo) => photo.url) || [],
|
|
8367
|
+
inReplyTo: currentTweet.inReplyToStatusId ? createUniqueUuid5(this.runtime, currentTweet.inReplyToStatusId) : void 0
|
|
8368
|
+
},
|
|
8369
|
+
createdAt: currentTweet.timestamp * 1e3,
|
|
8370
|
+
roomId,
|
|
8371
|
+
entityId: currentTweet.userId === this.twitterUserId ? this.runtime.agentId : createUniqueUuid5(this.runtime, currentTweet.userId)
|
|
8858
8372
|
},
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
entityId: currentTweet.userId === this.twitterUserId ? this.runtime.agentId : createUniqueUuid4(this.runtime, currentTweet.userId)
|
|
8862
|
-
});
|
|
8373
|
+
"messages"
|
|
8374
|
+
);
|
|
8863
8375
|
}
|
|
8864
8376
|
if (visited.has(currentTweet.id)) {
|
|
8865
|
-
|
|
8377
|
+
logger7.log("Already visited tweet:", currentTweet.id);
|
|
8866
8378
|
return;
|
|
8867
8379
|
}
|
|
8868
8380
|
visited.add(currentTweet.id);
|
|
8869
8381
|
thread.unshift(currentTweet);
|
|
8870
8382
|
if (currentTweet.inReplyToStatusId) {
|
|
8871
|
-
|
|
8383
|
+
logger7.log("Fetching parent tweet:", currentTweet.inReplyToStatusId);
|
|
8872
8384
|
try {
|
|
8873
|
-
const parentTweet = await this.twitterClient.getTweet(
|
|
8874
|
-
currentTweet.inReplyToStatusId
|
|
8875
|
-
);
|
|
8385
|
+
const parentTweet = await this.twitterClient.getTweet(currentTweet.inReplyToStatusId);
|
|
8876
8386
|
if (parentTweet) {
|
|
8877
|
-
|
|
8387
|
+
logger7.log("Found parent tweet:", {
|
|
8878
8388
|
id: parentTweet.id,
|
|
8879
8389
|
text: parentTweet.text?.slice(0, 50)
|
|
8880
8390
|
});
|
|
8881
8391
|
await processThread(parentTweet, depth + 1);
|
|
8882
8392
|
} else {
|
|
8883
|
-
|
|
8884
|
-
"No parent tweet found for:",
|
|
8885
|
-
currentTweet.inReplyToStatusId
|
|
8886
|
-
);
|
|
8393
|
+
logger7.log("No parent tweet found for:", currentTweet.inReplyToStatusId);
|
|
8887
8394
|
}
|
|
8888
8395
|
} catch (error) {
|
|
8889
|
-
|
|
8396
|
+
logger7.log("Error fetching parent tweet:", {
|
|
8890
8397
|
tweetId: currentTweet.inReplyToStatusId,
|
|
8891
8398
|
error
|
|
8892
8399
|
});
|
|
8893
8400
|
}
|
|
8894
8401
|
} else {
|
|
8895
|
-
|
|
8402
|
+
logger7.log("Reached end of reply chain at:", currentTweet.id);
|
|
8896
8403
|
}
|
|
8897
8404
|
}
|
|
8898
8405
|
await processThread.bind(this)(tweet, 0);
|
|
8899
8406
|
return thread;
|
|
8900
8407
|
}
|
|
8408
|
+
createMemoryObject(type, id, userId, roomId) {
|
|
8409
|
+
return {
|
|
8410
|
+
id: createUniqueUuid5(this.runtime, id),
|
|
8411
|
+
agentId: this.runtime.agentId,
|
|
8412
|
+
entityId: createUniqueUuid5(this.runtime, userId),
|
|
8413
|
+
roomId: createUniqueUuid5(this.runtime, roomId),
|
|
8414
|
+
content: {
|
|
8415
|
+
type,
|
|
8416
|
+
source: "twitter"
|
|
8417
|
+
},
|
|
8418
|
+
createdAt: Date.now()
|
|
8419
|
+
};
|
|
8420
|
+
}
|
|
8901
8421
|
};
|
|
8902
8422
|
|
|
8903
8423
|
// src/post.ts
|
|
8904
8424
|
import {
|
|
8905
|
-
ChannelType as
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
createUniqueUuid as createUniqueUuid5,
|
|
8910
|
-
extractAttributes,
|
|
8911
|
-
logger as logger7,
|
|
8912
|
-
parseJSONObjectFromText as parseJSONObjectFromText2,
|
|
8425
|
+
ChannelType as ChannelType6,
|
|
8426
|
+
EventType as EventType3,
|
|
8427
|
+
createUniqueUuid as createUniqueUuid6,
|
|
8428
|
+
logger as logger8,
|
|
8913
8429
|
truncateToCompleteSentence
|
|
8914
8430
|
} from "@elizaos/core";
|
|
8915
|
-
|
|
8916
|
-
// src/templates.ts
|
|
8917
|
-
var twitterPostTemplate = `# Task: Create a post in the voice and style and perspective of {{agentName}} @{{twitterUserName}}.
|
|
8918
|
-
{{providers}}
|
|
8919
|
-
Write a post that is {{adjective}} about {{topic}} (without mentioning {{topic}} directly), from the perspective of {{agentName}}. Do not add commentary or acknowledge this request, just write the post.
|
|
8920
|
-
Your response should be 1, 2, or 3 sentences (choose the length at random).
|
|
8921
|
-
Your response should not contain any questions. Brief, concise statements only. The total character count MUST be less than 280. No emojis. Use \\n\\n (double spaces) between statements if there are multiple statements in your response.`;
|
|
8922
|
-
|
|
8923
|
-
// src/post.ts
|
|
8924
8431
|
var TwitterPostClient = class {
|
|
8925
|
-
client;
|
|
8926
|
-
runtime;
|
|
8927
|
-
twitterUsername;
|
|
8928
|
-
isDryRun;
|
|
8929
|
-
state;
|
|
8930
8432
|
/**
|
|
8931
8433
|
* Constructor for initializing a new Twitter client with the provided client, runtime, and state
|
|
8932
8434
|
* @param {ClientBase} client - The client used for interacting with Twitter API
|
|
@@ -8939,62 +8441,41 @@ var TwitterPostClient = class {
|
|
|
8939
8441
|
this.runtime = runtime;
|
|
8940
8442
|
this.twitterUsername = state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
8941
8443
|
this.isDryRun = this.state?.TWITTER_DRY_RUN || this.runtime.getSetting("TWITTER_DRY_RUN");
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
`-
|
|
8444
|
+
logger8.log("Twitter Client Configuration:");
|
|
8445
|
+
logger8.log(`- Username: ${this.twitterUsername}`);
|
|
8446
|
+
logger8.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
|
|
8447
|
+
logger8.log(
|
|
8448
|
+
`- Auto-post: ${this.state?.TWITTER_ENABLE_POST_GENERATION || this.runtime.getSetting("TWITTER_ENABLE_POST_GENERATION") ? "disabled" : "enabled"}`
|
|
8947
8449
|
);
|
|
8948
|
-
|
|
8450
|
+
logger8.log(
|
|
8949
8451
|
`- Post Interval: ${this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN")}-${this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX")} minutes`
|
|
8950
8452
|
);
|
|
8951
|
-
|
|
8453
|
+
logger8.log(
|
|
8952
8454
|
`- Post Immediately: ${this.state?.TWITTER_POST_IMMEDIATELY || this.runtime.getSetting("TWITTER_POST_IMMEDIATELY") ? "enabled" : "disabled"}`
|
|
8953
8455
|
);
|
|
8954
8456
|
if (this.isDryRun) {
|
|
8955
|
-
|
|
8956
|
-
"Twitter client initialized in dry run mode - no actual tweets should be posted"
|
|
8957
|
-
);
|
|
8457
|
+
logger8.log("Twitter client initialized in dry run mode - no actual tweets should be posted");
|
|
8958
8458
|
}
|
|
8959
8459
|
}
|
|
8960
8460
|
/**
|
|
8961
|
-
*
|
|
8962
|
-
* If the client's profile is not available, it initializes the client first.
|
|
8963
|
-
* It generates a new tweet at random intervals specified by the TWITTER_POST_INTERVAL_MIN and TWITTER_POST_INTERVAL_MAX settings or state properties.
|
|
8964
|
-
* Optionally, it can immediately generate a tweet if TWITTER_POST_IMMEDIATELY is set to true in the state or settings.
|
|
8461
|
+
* Starts the Twitter post client, setting up a loop to periodically generate new tweets.
|
|
8965
8462
|
*/
|
|
8966
8463
|
async start() {
|
|
8967
|
-
|
|
8968
|
-
|
|
8464
|
+
logger8.log("Starting Twitter post client...");
|
|
8465
|
+
const tweetGeneration = this.runtime.getSetting("TWITTER_ENABLE_TWEET_GENERATION");
|
|
8466
|
+
if (tweetGeneration === false) {
|
|
8467
|
+
logger8.log("Tweet generation is disabled");
|
|
8468
|
+
return;
|
|
8969
8469
|
}
|
|
8970
8470
|
const generateNewTweetLoop = async () => {
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
timestamp: 0
|
|
8975
|
-
});
|
|
8976
|
-
}
|
|
8977
|
-
const lastPostTimestamp = lastPost.timestamp ?? 0;
|
|
8978
|
-
const minMinutes = (this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN")) ?? 90;
|
|
8979
|
-
const maxMinutes = (this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX")) ?? 180;
|
|
8980
|
-
const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes;
|
|
8981
|
-
const delay = randomMinutes * 60 * 1e3;
|
|
8982
|
-
if (Date.now() > lastPostTimestamp + delay) {
|
|
8983
|
-
await this.generateNewTweet();
|
|
8984
|
-
}
|
|
8985
|
-
setTimeout(() => {
|
|
8986
|
-
generateNewTweetLoop();
|
|
8987
|
-
}, delay);
|
|
8988
|
-
logger7.log(`Next tweet scheduled in ${randomMinutes} minutes`);
|
|
8471
|
+
const interval = (this.state?.TWITTER_POST_INTERVAL || this.runtime.getSetting("TWITTER_POST_INTERVAL") || 30) * 60 * 1e3;
|
|
8472
|
+
this.generateNewTweet();
|
|
8473
|
+
setTimeout(generateNewTweetLoop, interval);
|
|
8989
8474
|
};
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
}, 5e3);
|
|
8995
|
-
}
|
|
8996
|
-
generateNewTweetLoop();
|
|
8997
|
-
logger7.log("Tweet generation loop started");
|
|
8475
|
+
setTimeout(generateNewTweetLoop, 60 * 1e3);
|
|
8476
|
+
if (this.runtime.getSetting("TWITTER_POST_IMMEDIATELY")) {
|
|
8477
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
8478
|
+
this.generateNewTweet();
|
|
8998
8479
|
}
|
|
8999
8480
|
}
|
|
9000
8481
|
/**
|
|
@@ -9035,32 +8516,35 @@ var TwitterPostClient = class {
|
|
|
9035
8516
|
* @param {string} rawTweetContent - The raw content of the tweet.
|
|
9036
8517
|
*/
|
|
9037
8518
|
async processAndCacheTweet(runtime, client, tweet, roomId, rawTweetContent) {
|
|
9038
|
-
await runtime.
|
|
8519
|
+
await runtime.setCache(`twitter/${client.profile.username}/lastPost`, {
|
|
9039
8520
|
id: tweet.id,
|
|
9040
8521
|
timestamp: Date.now()
|
|
9041
8522
|
});
|
|
9042
8523
|
await client.cacheTweet(tweet);
|
|
9043
|
-
|
|
8524
|
+
logger8.log(`Tweet posted:
|
|
9044
8525
|
${tweet.permanentUrl}`);
|
|
9045
8526
|
await runtime.ensureRoomExists({
|
|
9046
8527
|
id: roomId,
|
|
9047
8528
|
name: "Twitter Feed",
|
|
9048
8529
|
source: "twitter",
|
|
9049
|
-
type:
|
|
8530
|
+
type: ChannelType6.FEED
|
|
9050
8531
|
});
|
|
9051
8532
|
await runtime.ensureParticipantInRoom(runtime.agentId, roomId);
|
|
9052
|
-
await runtime.
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
8533
|
+
await runtime.createMemory(
|
|
8534
|
+
{
|
|
8535
|
+
id: createUniqueUuid6(this.runtime, tweet.id),
|
|
8536
|
+
entityId: runtime.agentId,
|
|
8537
|
+
agentId: runtime.agentId,
|
|
8538
|
+
content: {
|
|
8539
|
+
text: rawTweetContent.trim(),
|
|
8540
|
+
url: tweet.permanentUrl,
|
|
8541
|
+
source: "twitter"
|
|
8542
|
+
},
|
|
8543
|
+
roomId,
|
|
8544
|
+
createdAt: tweet.timestamp
|
|
9060
8545
|
},
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
});
|
|
8546
|
+
"messages"
|
|
8547
|
+
);
|
|
9064
8548
|
}
|
|
9065
8549
|
/**
|
|
9066
8550
|
* Handles sending a note tweet with optional media data.
|
|
@@ -9102,12 +8586,12 @@ var TwitterPostClient = class {
|
|
|
9102
8586
|
);
|
|
9103
8587
|
const body = await standardTweetResult.json();
|
|
9104
8588
|
if (!body?.data?.create_tweet?.tweet_results?.result) {
|
|
9105
|
-
|
|
8589
|
+
logger8.error("Error sending tweet; Bad response:", body);
|
|
9106
8590
|
return;
|
|
9107
8591
|
}
|
|
9108
8592
|
return body.data.create_tweet.tweet_results.result;
|
|
9109
8593
|
} catch (error) {
|
|
9110
|
-
|
|
8594
|
+
logger8.error("Error sending standard Tweet:", error);
|
|
9111
8595
|
throw error;
|
|
9112
8596
|
}
|
|
9113
8597
|
}
|
|
@@ -9125,124 +8609,110 @@ var TwitterPostClient = class {
|
|
|
9125
8609
|
*/
|
|
9126
8610
|
async postTweet(runtime, client, tweetTextForPosting, roomId, rawTweetContent, twitterUsername, mediaData) {
|
|
9127
8611
|
try {
|
|
9128
|
-
|
|
8612
|
+
logger8.log("Posting new tweet:\n");
|
|
9129
8613
|
let result;
|
|
9130
8614
|
if (tweetTextForPosting.length > 280 - 1) {
|
|
9131
|
-
result = await this.handleNoteTweet(
|
|
9132
|
-
client,
|
|
9133
|
-
tweetTextForPosting,
|
|
9134
|
-
void 0,
|
|
9135
|
-
mediaData
|
|
9136
|
-
);
|
|
8615
|
+
result = await this.handleNoteTweet(client, tweetTextForPosting, void 0, mediaData);
|
|
9137
8616
|
} else {
|
|
9138
|
-
result = await this.sendStandardTweet(
|
|
9139
|
-
client,
|
|
9140
|
-
tweetTextForPosting,
|
|
9141
|
-
void 0,
|
|
9142
|
-
mediaData
|
|
9143
|
-
);
|
|
8617
|
+
result = await this.sendStandardTweet(client, tweetTextForPosting, void 0, mediaData);
|
|
9144
8618
|
}
|
|
9145
8619
|
const tweet = this.createTweetObject(result, client, twitterUsername);
|
|
9146
|
-
await this.processAndCacheTweet(
|
|
9147
|
-
runtime,
|
|
9148
|
-
client,
|
|
9149
|
-
tweet,
|
|
9150
|
-
roomId,
|
|
9151
|
-
rawTweetContent
|
|
9152
|
-
);
|
|
8620
|
+
await this.processAndCacheTweet(runtime, client, tweet, roomId, rawTweetContent);
|
|
9153
8621
|
} catch (error) {
|
|
9154
|
-
|
|
8622
|
+
logger8.error("Error sending tweet:");
|
|
9155
8623
|
throw error;
|
|
9156
8624
|
}
|
|
9157
8625
|
}
|
|
9158
8626
|
/**
|
|
9159
|
-
*
|
|
9160
|
-
|
|
9161
|
-
/**
|
|
9162
|
-
* Asynchronously generates a new tweet for the Twitter account associated with the agent.
|
|
9163
|
-
* This method retrieves random topics of interest from the character's topics list and prompts the user to compose a tweet based on those topics.
|
|
9164
|
-
* The tweet is then processed and posted on Twitter with optional media attachments.
|
|
9165
|
-
* @returns {void}
|
|
8627
|
+
* Handles the creation and posting of a tweet by emitting standardized events.
|
|
8628
|
+
* This approach aligns with our platform-independent architecture.
|
|
9166
8629
|
*/
|
|
9167
8630
|
async generateNewTweet() {
|
|
9168
|
-
logger7.log("Generating new tweet");
|
|
9169
8631
|
try {
|
|
9170
|
-
const
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
8632
|
+
const userId = this.client.profile?.id;
|
|
8633
|
+
if (!userId) {
|
|
8634
|
+
logger8.error("Cannot generate tweet: Twitter profile not available");
|
|
8635
|
+
return;
|
|
8636
|
+
}
|
|
8637
|
+
const worldId = createUniqueUuid6(this.runtime, userId);
|
|
8638
|
+
const roomId = createUniqueUuid6(this.runtime, `${userId}-home`);
|
|
8639
|
+
const callback = async (content) => {
|
|
8640
|
+
try {
|
|
8641
|
+
if (this.isDryRun) {
|
|
8642
|
+
logger8.info(`[DRY RUN] Would post tweet: ${content.text}`);
|
|
8643
|
+
return [];
|
|
8644
|
+
}
|
|
8645
|
+
const result = await this.postToTwitter(content.text, content.mediaData);
|
|
8646
|
+
const tweetId = result.rest_id || result.id_str || result.legacy?.id_str;
|
|
8647
|
+
if (result) {
|
|
8648
|
+
const postedTweetId = createUniqueUuid6(this.runtime, tweetId);
|
|
8649
|
+
const postedMemory = {
|
|
8650
|
+
id: postedTweetId,
|
|
8651
|
+
entityId: this.runtime.agentId,
|
|
8652
|
+
agentId: this.runtime.agentId,
|
|
8653
|
+
roomId,
|
|
8654
|
+
content: {
|
|
8655
|
+
...content,
|
|
8656
|
+
source: "twitter",
|
|
8657
|
+
channelType: ChannelType6.FEED,
|
|
8658
|
+
type: "post",
|
|
8659
|
+
metadata: {
|
|
8660
|
+
tweetId,
|
|
8661
|
+
postedAt: Date.now()
|
|
8662
|
+
}
|
|
8663
|
+
},
|
|
8664
|
+
createdAt: Date.now()
|
|
8665
|
+
};
|
|
8666
|
+
await this.runtime.createMemory(postedMemory, "messages");
|
|
8667
|
+
return [postedMemory];
|
|
8668
|
+
}
|
|
8669
|
+
return [];
|
|
8670
|
+
} catch (error) {
|
|
8671
|
+
logger8.error("Error posting tweet:", error);
|
|
8672
|
+
return [];
|
|
9179
8673
|
}
|
|
9180
|
-
});
|
|
9181
|
-
state.values = {
|
|
9182
|
-
...state.values,
|
|
9183
|
-
twitterUserName: this.client.profile.username
|
|
9184
8674
|
};
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
const response = await this.runtime.useModel(ModelTypes5.TEXT_SMALL, {
|
|
9192
|
-
prompt
|
|
8675
|
+
this.runtime.emitEvent([EventType3.POST_GENERATED, "TWITTER_POST_GENERATED" /* POST_GENERATED */], {
|
|
8676
|
+
runtime: this.runtime,
|
|
8677
|
+
callback,
|
|
8678
|
+
worldId,
|
|
8679
|
+
userId,
|
|
8680
|
+
roomId
|
|
9193
8681
|
});
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
8682
|
+
} catch (error) {
|
|
8683
|
+
logger8.error("Error generating tweet:", error);
|
|
8684
|
+
}
|
|
8685
|
+
}
|
|
8686
|
+
/**
|
|
8687
|
+
* Posts content to Twitter
|
|
8688
|
+
* @param {string} text The tweet text to post
|
|
8689
|
+
* @param {MediaData[]} mediaData Optional media to attach to the tweet
|
|
8690
|
+
* @returns {Promise<any>} The result from the Twitter API
|
|
8691
|
+
*/
|
|
8692
|
+
async postToTwitter(text, mediaData = []) {
|
|
8693
|
+
try {
|
|
8694
|
+
const mediaIds = [];
|
|
8695
|
+
if (mediaData && mediaData.length > 0) {
|
|
8696
|
+
for (const media of mediaData) {
|
|
8697
|
+
try {
|
|
8698
|
+
logger8.warn("Media upload not currently supported with the modern Twitter API");
|
|
8699
|
+
} catch (error) {
|
|
8700
|
+
logger8.error("Error uploading media:", error);
|
|
8701
|
+
}
|
|
9213
8702
|
}
|
|
9214
8703
|
}
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
}
|
|
9218
|
-
tweetTextForPosting = truncateToCompleteSentence(
|
|
9219
|
-
tweetTextForPosting,
|
|
9220
|
-
280 - 1
|
|
8704
|
+
const result = await this.client.requestQueue.add(
|
|
8705
|
+
() => this.client.twitterClient.sendTweet(text.substring(0, 280))
|
|
9221
8706
|
);
|
|
9222
|
-
const
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
logger7.info(`Dry run: would have posted tweet: ${tweetTextForPosting}`);
|
|
9227
|
-
return;
|
|
9228
|
-
}
|
|
9229
|
-
try {
|
|
9230
|
-
logger7.log(`Posting new tweet:
|
|
9231
|
-
${tweetTextForPosting}`);
|
|
9232
|
-
this.postTweet(
|
|
9233
|
-
this.runtime,
|
|
9234
|
-
this.client,
|
|
9235
|
-
tweetTextForPosting,
|
|
9236
|
-
roomId,
|
|
9237
|
-
rawTweetContent,
|
|
9238
|
-
this.twitterUsername,
|
|
9239
|
-
mediaData
|
|
9240
|
-
);
|
|
9241
|
-
} catch (error) {
|
|
9242
|
-
logger7.error("Error sending tweet:", error);
|
|
8707
|
+
const body = await result.json();
|
|
8708
|
+
if (!body?.data?.create_tweet?.tweet_results?.result) {
|
|
8709
|
+
logger8.error("Error sending tweet; Bad response:", body);
|
|
8710
|
+
return null;
|
|
9243
8711
|
}
|
|
8712
|
+
return body.data.create_tweet.tweet_results.result;
|
|
9244
8713
|
} catch (error) {
|
|
9245
|
-
|
|
8714
|
+
logger8.error("Error posting to Twitter:", error);
|
|
8715
|
+
throw error;
|
|
9246
8716
|
}
|
|
9247
8717
|
}
|
|
9248
8718
|
async stop() {
|
|
@@ -9251,18 +8721,11 @@ ${prompt}`);
|
|
|
9251
8721
|
|
|
9252
8722
|
// src/tests.ts
|
|
9253
8723
|
import {
|
|
9254
|
-
|
|
9255
|
-
createUniqueUuid as
|
|
9256
|
-
logger as
|
|
8724
|
+
ModelType as ModelType6,
|
|
8725
|
+
createUniqueUuid as createUniqueUuid7,
|
|
8726
|
+
logger as logger9,
|
|
9257
8727
|
stringToUuid as stringToUuid2
|
|
9258
8728
|
} from "@elizaos/core";
|
|
9259
|
-
|
|
9260
|
-
// src/types.ts
|
|
9261
|
-
var ServiceTypes2 = {
|
|
9262
|
-
TWITTER: "twitter"
|
|
9263
|
-
};
|
|
9264
|
-
|
|
9265
|
-
// src/tests.ts
|
|
9266
8729
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
9267
8730
|
var TEST_IMAGE = {
|
|
9268
8731
|
id: "mock-image-id",
|
|
@@ -9275,14 +8738,13 @@ var TEST_IMAGE = {
|
|
|
9275
8738
|
alt_text: "mock image"
|
|
9276
8739
|
};
|
|
9277
8740
|
var TwitterTestSuite = class {
|
|
9278
|
-
name = "twitter";
|
|
9279
|
-
twitterClient = null;
|
|
9280
|
-
tests;
|
|
9281
8741
|
/**
|
|
9282
8742
|
* Constructor for TestSuite class.
|
|
9283
8743
|
* Initializes an array of test functions to be executed.
|
|
9284
8744
|
*/
|
|
9285
8745
|
constructor() {
|
|
8746
|
+
this.name = "twitter";
|
|
8747
|
+
this.twitterClient = null;
|
|
9286
8748
|
this.tests = [
|
|
9287
8749
|
{
|
|
9288
8750
|
name: "Initialize Twitter Client",
|
|
@@ -9315,16 +8777,14 @@ var TwitterTestSuite = class {
|
|
|
9315
8777
|
*/
|
|
9316
8778
|
async testInitializingClient(runtime) {
|
|
9317
8779
|
try {
|
|
9318
|
-
const manager = runtime.getService(
|
|
8780
|
+
const manager = runtime.getService(ServiceType2.TWITTER);
|
|
9319
8781
|
if (!manager) {
|
|
9320
8782
|
throw new Error("Twitter client manager not found");
|
|
9321
8783
|
}
|
|
9322
8784
|
const clientId = stringToUuid2("default");
|
|
9323
|
-
this.twitterClient = manager.clients.get(
|
|
9324
|
-
manager.getClientKey(clientId, runtime.agentId)
|
|
9325
|
-
);
|
|
8785
|
+
this.twitterClient = manager.clients.get(manager.getClientKey(clientId, runtime.agentId));
|
|
9326
8786
|
if (this.twitterClient) {
|
|
9327
|
-
|
|
8787
|
+
logger9.debug("TwitterClient initialized successfully.");
|
|
9328
8788
|
} else {
|
|
9329
8789
|
throw new Error("TwitterClient failed to initialize.");
|
|
9330
8790
|
}
|
|
@@ -9345,7 +8805,7 @@ var TwitterTestSuite = class {
|
|
|
9345
8805
|
if (!profile || !profile.id) {
|
|
9346
8806
|
throw new Error("Profile fetch failed.");
|
|
9347
8807
|
}
|
|
9348
|
-
|
|
8808
|
+
logger9.log("Successfully fetched Twitter profile:", profile);
|
|
9349
8809
|
} catch (error) {
|
|
9350
8810
|
throw new Error(`Error fetching Twitter profile: ${error}`);
|
|
9351
8811
|
}
|
|
@@ -9364,9 +8824,7 @@ var TwitterTestSuite = class {
|
|
|
9364
8824
|
5,
|
|
9365
8825
|
1 /* Latest */
|
|
9366
8826
|
);
|
|
9367
|
-
|
|
9368
|
-
`Successfully fetched ${tweets.tweets.length} search tweets.`
|
|
9369
|
-
);
|
|
8827
|
+
logger9.debug(`Successfully fetched ${tweets.tweets.length} search tweets.`);
|
|
9370
8828
|
} catch (error) {
|
|
9371
8829
|
throw new Error(`Error fetching search tweets: ${error}`);
|
|
9372
8830
|
}
|
|
@@ -9384,9 +8842,7 @@ var TwitterTestSuite = class {
|
|
|
9384
8842
|
if (!timeline || timeline.length === 0) {
|
|
9385
8843
|
throw new Error("No tweets in home timeline.");
|
|
9386
8844
|
}
|
|
9387
|
-
|
|
9388
|
-
`Successfully fetched ${timeline.length} tweets from home timeline.`
|
|
9389
|
-
);
|
|
8845
|
+
logger9.log(`Successfully fetched ${timeline.length} tweets from home timeline.`);
|
|
9390
8846
|
} catch (error) {
|
|
9391
8847
|
throw new Error(`Error fetching home timeline: ${error}`);
|
|
9392
8848
|
}
|
|
@@ -9403,7 +8859,7 @@ var TwitterTestSuite = class {
|
|
|
9403
8859
|
if (!posts || posts.length === 0) {
|
|
9404
8860
|
throw new Error("No own posts found.");
|
|
9405
8861
|
}
|
|
9406
|
-
|
|
8862
|
+
logger9.log(`Successfully fetched ${posts.length} own posts.`);
|
|
9407
8863
|
} catch (error) {
|
|
9408
8864
|
throw new Error(`Error fetching own posts: ${error}`);
|
|
9409
8865
|
}
|
|
@@ -9417,7 +8873,7 @@ var TwitterTestSuite = class {
|
|
|
9417
8873
|
*/
|
|
9418
8874
|
async testPostTweet(runtime) {
|
|
9419
8875
|
try {
|
|
9420
|
-
const roomId =
|
|
8876
|
+
const roomId = createUniqueUuid7(runtime, "twitter_mock_room");
|
|
9421
8877
|
const postClient = this.twitterClient.post;
|
|
9422
8878
|
const tweetText = await this.generateRandomTweetContent(runtime);
|
|
9423
8879
|
await postClient.postTweet(
|
|
@@ -9428,7 +8884,7 @@ var TwitterTestSuite = class {
|
|
|
9428
8884
|
tweetText,
|
|
9429
8885
|
"test-username"
|
|
9430
8886
|
);
|
|
9431
|
-
|
|
8887
|
+
logger9.success("Successfully posted a test tweet.");
|
|
9432
8888
|
} catch (error) {
|
|
9433
8889
|
throw new Error(`Error posting a tweet: ${error}`);
|
|
9434
8890
|
}
|
|
@@ -9442,12 +8898,9 @@ var TwitterTestSuite = class {
|
|
|
9442
8898
|
*/
|
|
9443
8899
|
async testPostImageTweet(runtime) {
|
|
9444
8900
|
try {
|
|
9445
|
-
const roomId =
|
|
8901
|
+
const roomId = createUniqueUuid7(runtime, "twitter_mock_room");
|
|
9446
8902
|
const postClient = this.twitterClient.post;
|
|
9447
|
-
const tweetText = await this.generateRandomTweetContent(
|
|
9448
|
-
runtime,
|
|
9449
|
-
"image_post"
|
|
9450
|
-
);
|
|
8903
|
+
const tweetText = await this.generateRandomTweetContent(runtime, "image_post");
|
|
9451
8904
|
const mediaData = await fetchMediaData([TEST_IMAGE]);
|
|
9452
8905
|
await postClient.postTweet(
|
|
9453
8906
|
runtime,
|
|
@@ -9458,7 +8911,7 @@ var TwitterTestSuite = class {
|
|
|
9458
8911
|
"test-username",
|
|
9459
8912
|
mediaData
|
|
9460
8913
|
);
|
|
9461
|
-
|
|
8914
|
+
logger9.success("Successfully posted a test tweet.");
|
|
9462
8915
|
} catch (error) {
|
|
9463
8916
|
throw new Error(`Error posting a tweet: ${error}`);
|
|
9464
8917
|
}
|
|
@@ -9474,7 +8927,7 @@ var TwitterTestSuite = class {
|
|
|
9474
8927
|
try {
|
|
9475
8928
|
const postClient = this.twitterClient.post;
|
|
9476
8929
|
await postClient.generateNewTweet();
|
|
9477
|
-
|
|
8930
|
+
logger9.success("Successfully generated a new tweet.");
|
|
9478
8931
|
} catch (error) {
|
|
9479
8932
|
throw new Error(`Error generating new tweet: ${error}`);
|
|
9480
8933
|
}
|
|
@@ -9508,12 +8961,12 @@ var TwitterTestSuite = class {
|
|
|
9508
8961
|
message: {
|
|
9509
8962
|
content: { text: testTweet.text, source: "twitter" },
|
|
9510
8963
|
agentId: runtime.agentId,
|
|
9511
|
-
entityId:
|
|
9512
|
-
roomId:
|
|
8964
|
+
entityId: createUniqueUuid7(runtime, testTweet.entityId),
|
|
8965
|
+
roomId: createUniqueUuid7(runtime, testTweet.conversationId)
|
|
9513
8966
|
},
|
|
9514
8967
|
thread: []
|
|
9515
8968
|
});
|
|
9516
|
-
|
|
8969
|
+
logger9.success("Correct response decision.");
|
|
9517
8970
|
} catch (error) {
|
|
9518
8971
|
throw new Error(`Error handling tweet response: ${error}`);
|
|
9519
8972
|
}
|
|
@@ -9545,7 +8998,7 @@ var TwitterTestSuite = class {
|
|
|
9545
8998
|
- A lighthearted take on everyday situations.
|
|
9546
8999
|
Do not include hashtags or emojis.`;
|
|
9547
9000
|
}
|
|
9548
|
-
return await runtime.useModel(
|
|
9001
|
+
return await runtime.useModel(ModelType6.TEXT_SMALL, {
|
|
9549
9002
|
prompt
|
|
9550
9003
|
});
|
|
9551
9004
|
}
|
|
@@ -9553,30 +9006,22 @@ var TwitterTestSuite = class {
|
|
|
9553
9006
|
|
|
9554
9007
|
// src/index.ts
|
|
9555
9008
|
var TwitterClientInstance = class {
|
|
9556
|
-
client;
|
|
9557
|
-
post;
|
|
9558
|
-
interaction;
|
|
9559
|
-
space;
|
|
9560
|
-
service;
|
|
9561
9009
|
constructor(runtime, state) {
|
|
9562
9010
|
this.client = new ClientBase(runtime, state);
|
|
9563
9011
|
this.post = new TwitterPostClient(this.client, runtime, state);
|
|
9564
|
-
this.interaction = new TwitterInteractionClient(
|
|
9565
|
-
this.client,
|
|
9566
|
-
runtime,
|
|
9567
|
-
state
|
|
9568
|
-
);
|
|
9012
|
+
this.interaction = new TwitterInteractionClient(this.client, runtime, state);
|
|
9569
9013
|
if (runtime.getSetting("TWITTER_SPACES_ENABLE") === true) {
|
|
9570
9014
|
this.space = new TwitterSpaceClient(this.client, runtime);
|
|
9571
9015
|
}
|
|
9572
9016
|
this.service = TwitterService.getInstance();
|
|
9573
9017
|
}
|
|
9574
9018
|
};
|
|
9575
|
-
var
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9019
|
+
var _TwitterService = class _TwitterService extends Service {
|
|
9020
|
+
constructor() {
|
|
9021
|
+
super(...arguments);
|
|
9022
|
+
this.capabilityDescription = "The agent is able to send and receive messages on twitter";
|
|
9023
|
+
this.clients = /* @__PURE__ */ new Map();
|
|
9024
|
+
}
|
|
9580
9025
|
static getInstance() {
|
|
9581
9026
|
if (!_TwitterService.instance) {
|
|
9582
9027
|
_TwitterService.instance = new _TwitterService();
|
|
@@ -9584,14 +9029,13 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9584
9029
|
return _TwitterService.instance;
|
|
9585
9030
|
}
|
|
9586
9031
|
async createClient(runtime, clientId, state) {
|
|
9587
|
-
console.log("Creating client", clientId);
|
|
9588
9032
|
if (runtime.getSetting("TWITTER_2FA_SECRET") === null) {
|
|
9589
9033
|
runtime.setSetting("TWITTER_2FA_SECRET", void 0, false);
|
|
9590
9034
|
}
|
|
9591
9035
|
try {
|
|
9592
9036
|
const existingClient = this.getClient(clientId, runtime.agentId);
|
|
9593
9037
|
if (existingClient) {
|
|
9594
|
-
|
|
9038
|
+
logger10.info(`Twitter client already exists for ${clientId}`);
|
|
9595
9039
|
return existingClient;
|
|
9596
9040
|
}
|
|
9597
9041
|
const client = new TwitterClientInstance(runtime, state);
|
|
@@ -9606,13 +9050,91 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9606
9050
|
client.interaction.start();
|
|
9607
9051
|
}
|
|
9608
9052
|
this.clients.set(this.getClientKey(clientId, runtime.agentId), client);
|
|
9609
|
-
|
|
9053
|
+
await this.emitServerJoinedEvent(runtime, client);
|
|
9054
|
+
logger10.info(`Created Twitter client for ${clientId}`);
|
|
9610
9055
|
return client;
|
|
9611
9056
|
} catch (error) {
|
|
9612
|
-
|
|
9057
|
+
logger10.error(`Failed to create Twitter client for ${clientId}:`, error);
|
|
9613
9058
|
throw error;
|
|
9614
9059
|
}
|
|
9615
9060
|
}
|
|
9061
|
+
/**
|
|
9062
|
+
* Emits a standardized WORLD_JOINED event for Twitter
|
|
9063
|
+
* @param runtime The agent runtime
|
|
9064
|
+
* @param client The Twitter client instance
|
|
9065
|
+
*/
|
|
9066
|
+
async emitServerJoinedEvent(runtime, client) {
|
|
9067
|
+
try {
|
|
9068
|
+
if (!client.client.profile) {
|
|
9069
|
+
logger10.warn("Twitter profile not available yet, can't emit WORLD_JOINED event");
|
|
9070
|
+
return;
|
|
9071
|
+
}
|
|
9072
|
+
const profile = client.client.profile;
|
|
9073
|
+
const twitterId = profile.id;
|
|
9074
|
+
const username = profile.username;
|
|
9075
|
+
const worldId = createUniqueUuid8(runtime, twitterId);
|
|
9076
|
+
const world = {
|
|
9077
|
+
id: worldId,
|
|
9078
|
+
name: `${username}'s Twitter`,
|
|
9079
|
+
agentId: runtime.agentId,
|
|
9080
|
+
serverId: twitterId,
|
|
9081
|
+
metadata: {
|
|
9082
|
+
ownership: { ownerId: twitterId },
|
|
9083
|
+
roles: {
|
|
9084
|
+
[twitterId]: Role.OWNER
|
|
9085
|
+
},
|
|
9086
|
+
twitter: {
|
|
9087
|
+
username,
|
|
9088
|
+
id: twitterId
|
|
9089
|
+
}
|
|
9090
|
+
}
|
|
9091
|
+
};
|
|
9092
|
+
const homeTimelineRoomId = createUniqueUuid8(runtime, `${twitterId}-home`);
|
|
9093
|
+
const homeTimelineRoom = {
|
|
9094
|
+
id: homeTimelineRoomId,
|
|
9095
|
+
name: `${username}'s Timeline`,
|
|
9096
|
+
source: "twitter",
|
|
9097
|
+
type: ChannelType7.FEED,
|
|
9098
|
+
channelId: `${twitterId}-home`,
|
|
9099
|
+
serverId: twitterId,
|
|
9100
|
+
worldId
|
|
9101
|
+
};
|
|
9102
|
+
const mentionsRoomId = createUniqueUuid8(runtime, `${twitterId}-mentions`);
|
|
9103
|
+
const mentionsRoom = {
|
|
9104
|
+
id: mentionsRoomId,
|
|
9105
|
+
name: `${username}'s Mentions`,
|
|
9106
|
+
source: "twitter",
|
|
9107
|
+
type: ChannelType7.GROUP,
|
|
9108
|
+
channelId: `${twitterId}-mentions`,
|
|
9109
|
+
serverId: twitterId,
|
|
9110
|
+
worldId
|
|
9111
|
+
};
|
|
9112
|
+
const twitterUserId = createUniqueUuid8(runtime, twitterId);
|
|
9113
|
+
const twitterUser = {
|
|
9114
|
+
id: twitterUserId,
|
|
9115
|
+
names: [profile.screenName || username],
|
|
9116
|
+
agentId: runtime.agentId,
|
|
9117
|
+
metadata: {
|
|
9118
|
+
twitter: {
|
|
9119
|
+
id: twitterId,
|
|
9120
|
+
username,
|
|
9121
|
+
screenName: profile.screenName || username,
|
|
9122
|
+
name: profile.screenName || username
|
|
9123
|
+
}
|
|
9124
|
+
}
|
|
9125
|
+
};
|
|
9126
|
+
runtime.emitEvent(["TWITTER_WORLD_JOINED" /* WORLD_JOINED */, EventType4.WORLD_JOINED], {
|
|
9127
|
+
runtime,
|
|
9128
|
+
world,
|
|
9129
|
+
rooms: [homeTimelineRoom, mentionsRoom],
|
|
9130
|
+
users: [twitterUser],
|
|
9131
|
+
source: "twitter"
|
|
9132
|
+
});
|
|
9133
|
+
logger10.info(`Emitted WORLD_JOINED event for Twitter account ${username}`);
|
|
9134
|
+
} catch (error) {
|
|
9135
|
+
logger10.error("Failed to emit WORLD_JOINED event for Twitter:", error);
|
|
9136
|
+
}
|
|
9137
|
+
}
|
|
9616
9138
|
getClient(clientId, agentId) {
|
|
9617
9139
|
return this.clients.get(this.getClientKey(clientId, agentId));
|
|
9618
9140
|
}
|
|
@@ -9623,9 +9145,9 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9623
9145
|
try {
|
|
9624
9146
|
await client.service.stop();
|
|
9625
9147
|
this.clients.delete(key);
|
|
9626
|
-
|
|
9148
|
+
logger10.info(`Stopped Twitter client for ${clientId}`);
|
|
9627
9149
|
} catch (error) {
|
|
9628
|
-
|
|
9150
|
+
logger10.error(`Error stopping Twitter client for ${clientId}:`, error);
|
|
9629
9151
|
}
|
|
9630
9152
|
}
|
|
9631
9153
|
}
|
|
@@ -9643,15 +9165,11 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9643
9165
|
try {
|
|
9644
9166
|
if (config.TWITTER_USERNAME && // Basic auth
|
|
9645
9167
|
config.TWITTER_PASSWORD && config.TWITTER_EMAIL) {
|
|
9646
|
-
|
|
9647
|
-
await twitterClientManager.createClient(
|
|
9648
|
-
runtime,
|
|
9649
|
-
runtime.agentId,
|
|
9650
|
-
config
|
|
9651
|
-
);
|
|
9168
|
+
logger10.info("Creating default Twitter client from character settings");
|
|
9169
|
+
await twitterClientManager.createClient(runtime, runtime.agentId, config);
|
|
9652
9170
|
}
|
|
9653
9171
|
} catch (error) {
|
|
9654
|
-
|
|
9172
|
+
logger10.error("Failed to create default Twitter client:", error);
|
|
9655
9173
|
}
|
|
9656
9174
|
return twitterClientManager;
|
|
9657
9175
|
}
|
|
@@ -9664,7 +9182,7 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9664
9182
|
await client.service.stop();
|
|
9665
9183
|
this.clients.delete(key);
|
|
9666
9184
|
} catch (error) {
|
|
9667
|
-
|
|
9185
|
+
logger10.error(`Error stopping Twitter client ${key}:`, error);
|
|
9668
9186
|
}
|
|
9669
9187
|
}
|
|
9670
9188
|
}
|
|
@@ -9672,6 +9190,8 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9672
9190
|
return `${clientId}-${agentId}`;
|
|
9673
9191
|
}
|
|
9674
9192
|
};
|
|
9193
|
+
_TwitterService.serviceType = TWITTER_SERVICE_NAME;
|
|
9194
|
+
var TwitterService = _TwitterService;
|
|
9675
9195
|
var twitterPlugin = {
|
|
9676
9196
|
name: TWITTER_SERVICE_NAME,
|
|
9677
9197
|
description: "Twitter client with per-server instance management",
|