@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 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
- logger as logger9
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 logger4,
13
+ logger as logger5,
10
14
  stringToUuid
11
15
  } from "@elizaos/core";
12
16
 
13
17
  // src/spaces.ts
14
- import { ModelTypes as ModelTypes3, logger as logger3 } from "@elizaos/core";
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-GVRE5HAO.js");
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 getUserIdByScreenName(screenName, auth) {
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
- (user) => ({
1001
- id: user.id_str,
1002
- screenName: user.screen_name,
1003
- name: user.name,
1004
- profileImageUrl: user.profile_image_url_https,
1005
- description: user.description,
1006
- verified: user.verified,
1007
- protected: user.protected,
1008
- followersCount: user.followers_count,
1009
- friendsCount: user.friends_count
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 getUserIdByScreenName(username, auth);
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
- "https://api.twitter.com/1.1/friendships/create.json"
1384
- );
1385
- const res = await auth.fetch(
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 getUserIdByScreenName(q, auth);
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 getUserIdByScreenName(q, auth);
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
- `${uploadUrl}?${statusParams.toString()}`,
3276
- {
3277
- method: "GET",
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 getUserIdByScreenName(screenName) {
3689
- const res = await getUserIdByScreenName(screenName, this.auth);
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, logger10, emitter) {
4565
+ function setupCommonChatEvents(chatClient, logger11, emitter) {
4761
4566
  chatClient.on("occupancyUpdate", (upd) => {
4762
- logger10.debug("[ChatEvents] occupancyUpdate =>", upd);
4567
+ logger11.debug("[ChatEvents] occupancyUpdate =>", upd);
4763
4568
  emitter.emit("occupancyUpdate", upd);
4764
4569
  });
4765
4570
  chatClient.on("guestReaction", (reaction) => {
4766
- logger10.debug("[ChatEvents] guestReaction =>", reaction);
4571
+ logger11.debug("[ChatEvents] guestReaction =>", reaction);
4767
4572
  emitter.emit("guestReaction", reaction);
4768
4573
  });
4769
4574
  chatClient.on("muteStateChanged", (evt) => {
4770
- logger10.debug("[ChatEvents] muteStateChanged =>", evt);
4575
+ logger11.debug("[ChatEvents] muteStateChanged =>", evt);
4771
4576
  emitter.emit("muteStateChanged", evt);
4772
4577
  });
4773
4578
  chatClient.on("speakerRequest", (req) => {
4774
- logger10.debug("[ChatEvents] speakerRequest =>", req);
4579
+ logger11.debug("[ChatEvents] speakerRequest =>", req);
4775
4580
  emitter.emit("speakerRequest", req);
4776
4581
  });
4777
4582
  chatClient.on("newSpeakerAccepted", (info) => {
4778
- logger10.debug("[ChatEvents] newSpeakerAccepted =>", info);
4583
+ logger11.debug("[ChatEvents] newSpeakerAccepted =>", info);
4779
4584
  emitter.emit("newSpeakerAccepted", info);
4780
4585
  });
4781
4586
  chatClient.on("newSpeakerRemoved", (info) => {
4782
- logger10.debug("[ChatEvents] newSpeakerRemoved =>", info);
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
- // The 'sender' field is not required, it's not even verified by the server
4886
- // Instead of passing attributes down here it's easier to ignore it
4887
- sender: {
4888
- user_id: null,
4889
- twitter_id: null,
4890
- username: null,
4891
- display_name: null,
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
- `${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`,
5407
- {
5408
- method: "POST",
5409
- headers: {
5410
- Authorization: this.config.credential,
5411
- "Content-Type": "application/json",
5412
- Referer: "https://x.com"
5413
- },
5414
- body: JSON.stringify({
5415
- janus: "message",
5416
- transaction,
5417
- body
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
- `${this.config.webrtcUrl}/${this.sessionId}/${handleId}`,
5507
- {
5508
- method: "POST",
5509
- headers: {
5510
- Authorization: this.config.credential,
5511
- "Content-Type": "application/json"
5512
- },
5513
- body: JSON.stringify({
5514
- janus: "message",
5515
- transaction,
5516
- body,
5517
- jsep
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
- `${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`,
5669
- {
5670
- method: "POST",
5671
- headers: {
5672
- Authorization: this.config.credential,
5673
- "Content-Type": "application/json",
5674
- Referer: "https://x.com"
5675
- },
5676
- body: JSON.stringify({
5677
- janus: "message",
5678
- transaction,
5679
- body
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
- `${this.config.webrtcUrl}/${this.sessionId}/${this.handleId}`,
5706
- {
5707
- method: "POST",
5708
- headers: {
5709
- Authorization: this.config.credential,
5710
- "Content-Type": "application/json"
5711
- },
5712
- body: JSON.stringify({
5713
- janus: "message",
5714
- transaction,
5715
- body
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
- ModelTypes,
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
- name = "SttTtsPlugin";
6588
- description = "Speech-to-text (OpenAI) + conversation + TTS (ElevenLabs)";
6589
- runtime;
6590
- spaceId;
6591
- space;
6592
- janus;
6593
- /**
6594
- * userId => arrayOfChunks (PCM Int16)
6595
- */
6596
- pcmBuffers = /* @__PURE__ */ new Map();
6597
- // TTS queue for sequentially speaking
6598
- ttsQueue = [];
6599
- isSpeaking = false;
6600
- isProcessingAudio = false;
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
- logger.log("[SttTtsPlugin] onAttach => space was attached");
6262
+ logger2.log("[SttTtsPlugin] onAttach => space was attached");
6606
6263
  }
6607
6264
  async init(params) {
6608
- logger.log(
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
- logger.log(
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) => logger.error("[SttTtsPlugin] handleSilence error =>", 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
- logger.log("[SttTtsPlugin] TTS playback interrupted");
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
- logger.log("[SttTtsPlugin] Starting audio processing for user:", userId);
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
- logger.warn("[SttTtsPlugin] No audio chunks for user =>", userId);
6378
+ logger2.warn("[SttTtsPlugin] No audio chunks for user =>", userId);
6727
6379
  return;
6728
6380
  }
6729
- logger.log(
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
- ModelTypes.TRANSCRIPTION,
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
- logger.warn("[SttTtsPlugin] No speech recognized for user =>", userId);
6393
+ logger2.warn("[SttTtsPlugin] No speech recognized for user =>", userId);
6747
6394
  return;
6748
6395
  }
6749
- logger.log(`[SttTtsPlugin] STT => user=${userId}, text="${sttText}"`);
6396
+ logger2.log(`[SttTtsPlugin] STT => user=${userId}, text="${sttText}"`);
6750
6397
  await this.handleUserMessage(sttText, userId);
6751
6398
  } catch (error) {
6752
- logger.error("[SttTtsPlugin] processAudio error =>", error);
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
- logger.error("[SttTtsPlugin] processTtsQueue error =>", err);
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
- logger.error("[SttTtsPlugin] TTS responseStream is null");
6428
+ logger2.error("[SttTtsPlugin] TTS responseStream is null");
6785
6429
  continue;
6786
6430
  }
6787
- logger.log("[SttTtsPlugin] Received ElevenLabs TTS stream");
6431
+ logger2.log("[SttTtsPlugin] Received ElevenLabs TTS stream");
6788
6432
  await this.streamTtsStreamToJanus(responseStream, 48e3, signal);
6789
6433
  if (signal.aborted) {
6790
- logger.log("[SttTtsPlugin] TTS interrupted after streaming");
6434
+ logger2.log("[SttTtsPlugin] TTS interrupted after streaming");
6791
6435
  return;
6792
6436
  }
6793
6437
  } catch (err) {
6794
- logger.error("[SttTtsPlugin] TTS streaming error =>", err);
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.getDatabaseAdapter().getEntityById(userUuid);
6455
+ const entity = await this.runtime.getEntityById(userUuid);
6815
6456
  if (!entity) {
6816
- await this.runtime.getDatabaseAdapter().createEntity({
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.getMemoryManager("messages").createMemory(responseMemory);
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(["VOICE_MESSAGE_RECEIVED"], {
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
- logger.log("[SttTtsPlugin] streamToJanus interrupted");
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
- logger.log("[SttTtsPlugin] Stream aborted, stopping playback");
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
- logger.log("[SttTtsPlugin] Stream ended but was aborted");
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
- logger.error("[SttTtsPlugin] Error in TTS stream", error);
6594
+ logger2.error("[SttTtsPlugin] Error in TTS stream", error);
6964
6595
  reject(error);
6965
6596
  });
6966
6597
  });
6967
6598
  }
6968
6599
  cleanup() {
6969
- logger.log("[SttTtsPlugin] cleanup => releasing resources");
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
- ModelTypes as ModelTypes2,
6614
+ ModelType as ModelType2,
6984
6615
  composePrompt,
6985
6616
  createUniqueUuid as createUniqueUuid2,
6986
- logger as logger2
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
- path.resolve(attachment.url)
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(ModelTypes2.TEXT_SMALL, {
6657
+ const output = await runtime.useModel(ModelType2.TEXT_SMALL, {
7315
6658
  prompt
7316
6659
  });
7317
6660
  return output.trim();
7318
6661
  } catch (err) {
7319
- logger2.error("[generateFiller] Error generating filler:", err);
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
- logger2.log(`[Space] Filler (${fillerType}) => ${text}`);
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(ModelTypes2.TEXT_SMALL, {
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
- logger2.error("[generateTopicsIfEmpty] GPT error =>", err);
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
- logger3.log("[Space] Starting periodic check routine...");
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
- logger3.error("[Space] Error in routine =>", error);
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
- logger3.log("[Space] Too soon since last space => skip");
6777
+ logger4.log("[Space] Too soon since last space => skip");
7447
6778
  return false;
7448
6779
  }
7449
6780
  }
7450
- logger3.log("[Space] Deciding to launch a new Space...");
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
- logger3.log("[Space] Starting a new Twitter Space...");
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
- if (this.runtime.getModel(ModelTypes3.TEXT_TO_SPEECH) && this.runtime.getModel(ModelTypes3.TRANSCRIPTION)) {
7481
- logger3.log("[Space] Using SttTtsPlugin");
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
- logger3.log("[Space] Using IdleMonitorPlugin");
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
- logger3.log(`[Space] Space started => ${spaceUrl}`);
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
- logger3.log(`[Space] Occupancy => ${update.occupancy} participant(s).`);
6865
+ logger4.log(`[Space] Occupancy => ${update.occupancy} participant(s).`);
7505
6866
  });
7506
6867
  this.currentSpace.on("speakerRequest", async (req) => {
7507
- logger3.log(
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
- logger3.log(`[Space] idleTimeout => no audio for ${info.idleMs} ms.`);
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
- logger3.log("[Space] SIGINT => stopping space");
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
- logger3.error("[Space] Error launching Space =>", error);
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
- logger3.log(
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
- logger3.log("[Space] More than maxSpeakers => removing extras...");
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
- logger3.log("[Space] Condition met => stopping the Space...");
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
- logger3.error("[Space] Error in manageCurrentSpace =>", error);
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
- logger3.log(`[Space] Accepting speaker @${req.username} now`);
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
- logger3.log(`[Space] Adding speaker @${req.username} to the queue`);
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
- logger3.log(`[Space] Speaker @${req.username} is now live`);
6961
+ logger4.log(`[Space] Speaker @${req.username} is now live`);
7620
6962
  } catch (err) {
7621
- logger3.error(`[Space] Error approving speaker @${req.username}:`, err);
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
- logger3.log(`[Space] Removed speaker userId=${userId}`);
6970
+ logger4.log(`[Space] Removed speaker userId=${userId}`);
7629
6971
  } catch (error) {
7630
- logger3.error(`[Space] Error removing speaker userId=${userId} =>`, error);
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
- logger3.log(`[Space] Removing extra speaker => userId=${sp.user_id}`);
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
- logger3.log("[Space] Stopping the current Space...");
6995
+ logger4.log("[Space] Stopping the current Space...");
7655
6996
  await this.currentSpace.stop();
7656
6997
  } catch (err) {
7657
- logger3.error("[Space] Error stopping Space =>", err);
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
- logger3.warn("currently hosting/participating a space");
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
- logger3.error(`failed to join space ${error}`);
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
- logger3.log(
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
- console.log("[SpaceParticipant] Speaker removed:", evt);
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
- console.log(
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
- logger3.log(
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
- logger3.error(
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
- console.log("[SpaceParticipant] Successfully became speaker!");
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
- logger4.error("State is not available.");
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(ServiceTypes.TWITTER);
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
- logger4.error("space action - no space manager found");
7162
+ logger5.error("space action - no space manager found");
7835
7163
  return false;
7836
7164
  }
7837
7165
  if (spaceManager.spaceStatus !== "idle" /* IDLE */) {
7838
- logger4.warn("currently hosting/participating a space");
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
- logger4.warn("space action - no tweet found in message");
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
- logger4.error("Error joining Twitter Space:", error);
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
- logger4.error(`Error fetching tweets for ${userName}:`, error);
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 ChannelType3,
7933
- createUniqueUuid as createUniqueUuid3,
7934
- logger as logger5
7259
+ ChannelType as ChannelType4,
7260
+ createUniqueUuid as createUniqueUuid4,
7261
+ logger as logger6
7935
7262
  } from "@elizaos/core";
7936
7263
  var RequestQueue = class {
7937
- queue = [];
7938
- processing = false;
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 ClientBase = class _ClientBase extends EventEmitter6 {
8002
- static _twitterClients = {};
8003
- twitterClient;
8004
- runtime;
8005
- lastCheckedTweetId = null;
8006
- temperature = 0.5;
8007
- requestQueue = new RequestQueue();
8008
- profile;
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.getDatabaseAdapter().setCache(`twitter/tweets/${tweet.id}`, tweet);
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.getDatabaseAdapter().getCache(`twitter/tweets/${tweetId}`);
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
- let retries = (this.state?.TWITTER_RETRY_LIMIT || this.runtime.getSetting(
8155
- "TWITTER_RETRY_LIMIT"
8156
- )) ?? 3;
8157
- if (!username) {
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
- logger5.info("Successfully logged in.");
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
- logger5.info("Successfully logged in.");
8188
- logger5.info("Caching cookies");
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
- logger5.error(`Login attempt failed: ${error.message}`);
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
- retries--;
8199
- logger5.error(
8200
- `Failed to login to Twitter. Retrying... (${retries} attempts left)`
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
- logger5.log("Twitter user ID:", this.profile.id);
8211
- logger5.log("Twitter loaded:", JSON.stringify(this.profile, null, 10));
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
- logger5.debug("fetching own posts");
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
- logger5.debug("fetching home timeline");
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
- logger5.error("Error fetching search tweets:", error);
7559
+ logger6.error("Error fetching search tweets:", error);
8263
7560
  return { tweets: [] };
8264
7561
  }
8265
7562
  } catch (error) {
8266
- logger5.error("Error fetching search tweets:", error);
7563
+ logger6.error("Error fetching search tweets:", error);
8267
7564
  return { tweets: [] };
8268
7565
  }
8269
7566
  }
8270
7567
  async populateTimeline() {
8271
- logger5.debug("populating timeline...");
7568
+ logger6.debug("populating timeline...");
8272
7569
  const cachedTimeline = await this.getCachedTimeline();
8273
7570
  if (cachedTimeline) {
8274
- const existingMemories2 = await this.runtime.getMemoryManager("messages").getMemoriesByRoomIds({
7571
+ const existingMemories2 = await this.runtime.getMemoriesByRoomIds({
7572
+ tableName: "messages",
8275
7573
  roomIds: cachedTimeline.map(
8276
- (tweet) => createUniqueUuid3(this.runtime, tweet.conversationId)
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(createUniqueUuid3(this.runtime, tweet.id))
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(createUniqueUuid3(this.runtime, tweet.id))
7583
+ (tweet) => tweet.userId !== this.profile.id && !existingMemoryIds2.has(createUniqueUuid4(this.runtime, tweet.id))
8288
7584
  );
8289
7585
  for (const tweet of tweetsToSave2) {
8290
- logger5.log("Saving Tweet", tweet.id);
7586
+ logger6.log("Saving Tweet", tweet.id);
8291
7587
  if (tweet.userId === this.profile.id) {
8292
7588
  continue;
8293
7589
  }
8294
- const roomId = createUniqueUuid3(this.runtime, tweet.conversationId);
8295
- const entityId = createUniqueUuid3(
8296
- this.runtime,
8297
- tweet.userId === this.profile.id ? this.runtime.agentId : tweet.userId
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: ChannelType3.FEED
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 ? createUniqueUuid3(this.runtime, tweet.inReplyToStatusId) : void 0
7628
+ inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
8312
7629
  };
8313
- logger5.log("Creating memory for tweet", tweet.id);
8314
- const memory = await this.runtime.getMemoryManager("messages").getMemoryById(createUniqueUuid3(this.runtime, tweet.id));
8315
- if (memory) {
8316
- logger5.log("Memory already exists, skipping timeline population");
8317
- break;
8318
- }
8319
- await this.runtime.getMemoryManager("messages").createMemory({
8320
- id: createUniqueUuid3(this.runtime, tweet.id),
8321
- entityId,
8322
- content,
8323
- agentId: this.runtime.agentId,
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
- logger5.log(
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(createUniqueUuid3(this.runtime, tweet.conversationId));
7659
+ roomIds.add(createUniqueUuid4(this.runtime, tweet.conversationId));
8348
7660
  }
8349
- const existingMemories = await this.runtime.getMemoryManager("messages").getMemoriesByRoomIds({
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(createUniqueUuid3(this.runtime, tweet.id))
7667
+ (tweet) => tweet.userId !== this.profile.id && !existingMemoryIds.has(createUniqueUuid4(this.runtime, tweet.id))
8357
7668
  );
8358
- logger5.debug({
7669
+ logger6.debug({
8359
7670
  processingTweets: tweetsToSave.map((tweet) => tweet.id).join(",")
8360
7671
  });
8361
7672
  for (const tweet of tweetsToSave) {
8362
- logger5.log("Saving Tweet", tweet.id);
7673
+ logger6.log("Saving Tweet", tweet.id);
8363
7674
  if (tweet.userId === this.profile.id) {
8364
7675
  continue;
8365
7676
  }
8366
- const roomId = createUniqueUuid3(this.runtime, tweet.conversationId);
8367
- const entityId = tweet.userId === this.profile.id ? this.runtime.agentId : createUniqueUuid3(this.runtime, tweet.userId);
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: ChannelType3.FEED
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 ? createUniqueUuid3(this.runtime, tweet.inReplyToStatusId) : void 0
7715
+ inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
8381
7716
  };
8382
- await this.runtime.getMemoryManager("messages").createMemory({
8383
- id: createUniqueUuid3(this.runtime, tweet.id),
8384
- entityId,
8385
- content,
8386
- agentId: this.runtime.agentId,
8387
- roomId,
8388
- createdAt: tweet.timestamp * 1e3
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.getMemoryManager("messages").getMemories({
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
- logger5.debug("Message already saved", recentMessage[0].id);
7748
+ logger6.debug("Message already saved", recentMessage[0].id);
8410
7749
  } else {
8411
- await this.runtime.getMemoryManager("messages").createMemory(message);
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.getDatabaseAdapter().getCache(
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.getDatabaseAdapter().setCache(
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.getDatabaseAdapter().getCache(`twitter/${this.profile.username}/timeline`);
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.getDatabaseAdapter().setCache(`twitter/${this.profile.username}/timeline`, timeline);
7784
+ await this.runtime.setCache(`twitter/${this.profile.username}/timeline`, timeline);
8444
7785
  }
8445
7786
  async cacheMentions(mentions) {
8446
- await this.runtime.getDatabaseAdapter().setCache(`twitter/${this.profile.username}/mentions`, mentions);
7787
+ await this.runtime.setCache(`twitter/${this.profile.username}/mentions`, mentions);
8447
7788
  }
8448
7789
  async getCachedCookies(username) {
8449
- const cached = await this.runtime.getDatabaseAdapter().getCache(`twitter/${username}/cookies`);
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.getDatabaseAdapter().setCache(`twitter/${username}/cookies`, cookies);
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 ChannelType4,
8484
- ModelTypes as ModelTypes4,
7851
+ ChannelType as ChannelType5,
7852
+ EventType as EventType2,
7853
+ ModelType as ModelType4,
8485
7854
  composePrompt as composePrompt2,
8486
- createUniqueUuid as createUniqueUuid4,
8487
- logger as logger6,
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
- The "action" field should be one of the options in [Available Actions] and the "text" field should be the response you want to send. Do not including any thinking or internal reflection in the "text" field. "thought" should be a short description of what the agent is thinking about before responding, inlcuding a brief justification for the response.`;
8503
- var twitterShouldRespondTemplate = `# INSTRUCTIONS: Determine if {{agentName}} (@{{twitterUserName}}) should respond to the message and participate in the conversation. Do not comment. Just respond with "true" or "false".
8504
-
8505
- Response options are RESPOND, IGNORE and STOP.
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
- # INSTRUCTIONS: Respond with RESPOND if {{agentName}} should respond, or IGNORE if {{agentName}} should not respond to the last message and STOP if {{agentName}} should stop participating in the conversation.
8530
- The available options are RESPOND, IGNORE, or STOP. Choose the most appropriate option.`;
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
- logger6.log("Checking Twitter interactions");
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
- `@${twitterUsername}`,
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 = createUniqueUuid4(this.runtime, tweet.id);
8583
- const existingResponse = await this.runtime.getMemoryManager("messages").getMemoryById(tweetId);
7921
+ const tweetId = createUniqueUuid5(this.runtime, tweet.id);
7922
+ const existingResponse = await this.runtime.getMemoryById(tweetId);
8584
7923
  if (existingResponse) {
8585
- logger6.log(`Already responded to tweet ${tweet.id}, skipping`);
7924
+ logger7.log(`Already responded to tweet ${tweet.id}, skipping`);
8586
7925
  continue;
8587
7926
  }
8588
- logger6.log("New Tweet found", tweet.permanentUrl);
8589
- const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
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: ChannelType4.GROUP
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 thread = await buildConversationThread(tweet, this.client);
8603
- const message = {
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
- logger6.log("Finished checking Twitter interactions");
8163
+ logger7.log("Finished checking Twitter interactions");
8624
8164
  } catch (error) {
8625
- logger6.error("Error handling Twitter interactions:", error);
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
- logger6.log("Skipping Tweet with no text", tweet.id);
8186
+ logger7.log("Skipping Tweet with no text", tweet.id);
8647
8187
  return { text: "", actions: ["IGNORE"] };
8648
8188
  }
8649
- logger6.log("Processing Tweet: ", tweet.id);
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
- logger6.error("Error Occured during describing image: ", error);
8212
+ logger7.error("Error Occured during describing image: ", error);
8678
8213
  }
8679
- let state = await this.runtime.composeState(message);
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 = createUniqueUuid4(this.runtime, tweet.id);
8688
- const tweetExists = await this.runtime.getMemoryManager("messages").getMemoryById(tweetId);
8221
+ const tweetId = createUniqueUuid5(this.runtime, tweet.id);
8222
+ const tweetExists = await this.runtime.getMemoryById(tweetId);
8689
8223
  if (!tweetExists) {
8690
- logger6.log("tweet does not exist, saving");
8691
- const entityId = createUniqueUuid4(this.runtime, tweet.userId);
8692
- const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
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: ChannelType4.GROUP
8233
+ type: ChannelType5.GROUP
8700
8234
  });
8701
- const message2 = {
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 ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
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(message2, state);
8258
+ this.client.saveRequestMessage(memory, state);
8715
8259
  }
8716
8260
  const shouldRespondPrompt = composePrompt2({
8717
8261
  state,
8718
- template: this.runtime.character.templates?.twitterShouldRespondTemplate || this.runtime.character?.templates?.shouldRespondTemplate || twitterShouldRespondTemplate
8262
+ template: this.runtime.character.templates?.shouldRespondTemplate || ""
8719
8263
  });
8720
- const shouldRespond = await this.runtime.useModel(ModelTypes4.TEXT_SMALL, {
8264
+ const response = await this.runtime.useModel(ModelType4.TEXT_SMALL, {
8721
8265
  prompt: shouldRespondPrompt
8722
8266
  });
8723
- if (!shouldRespond.includes("RESPOND")) {
8724
- logger6.log("Not responding to message");
8725
- return { text: "Response Decision:", action: shouldRespond };
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 prompt = composePrompt2({
8728
- state: {
8729
- ...state,
8730
- values: {
8731
- ...state.values,
8732
- // Convert actionNames array to string
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
- template: this.runtime.character.templates?.twitterMessageHandlerTemplate || this.runtime.character?.templates?.messageHandlerTemplate || twitterMessageHandlerTemplate
8744
- });
8745
- const responseText = await this.runtime.useModel(ModelTypes4.TEXT_LARGE, {
8746
- prompt
8747
- });
8748
- const response = parseJSONObjectFromText(responseText);
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
- } else {
8761
- try {
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
- logger6.log("Processing tweet:", {
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
- logger6.log("No current tweet found for thread building");
8337
+ logger7.log("No current tweet found for thread building");
8831
8338
  return;
8832
8339
  }
8833
8340
  if (depth >= maxReplies) {
8834
- logger6.log("Reached maximum reply depth", depth);
8341
+ logger7.log("Reached maximum reply depth", depth);
8835
8342
  return;
8836
8343
  }
8837
- const memory = await this.runtime.getMemoryManager("messages").getMemoryById(createUniqueUuid4(this.runtime, currentTweet.id));
8344
+ const memory = await this.runtime.getMemoryById(
8345
+ createUniqueUuid5(this.runtime, currentTweet.id)
8346
+ );
8838
8347
  if (!memory) {
8839
- const roomId = createUniqueUuid4(this.runtime, tweet.conversationId);
8840
- const entityId = createUniqueUuid4(this.runtime, currentTweet.userId);
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: ChannelType4.GROUP
8356
+ type: ChannelType5.GROUP
8848
8357
  });
8849
- this.runtime.getMemoryManager("messages").createMemory({
8850
- id: createUniqueUuid4(this.runtime, currentTweet.id),
8851
- agentId: this.runtime.agentId,
8852
- content: {
8853
- text: currentTweet.text,
8854
- source: "twitter",
8855
- url: currentTweet.permanentUrl,
8856
- imageUrls: currentTweet.photos?.map((photo) => photo.url) || [],
8857
- inReplyTo: currentTweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, currentTweet.inReplyToStatusId) : void 0
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
- createdAt: currentTweet.timestamp * 1e3,
8860
- roomId,
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
- logger6.log("Already visited tweet:", currentTweet.id);
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
- logger6.log("Fetching parent tweet:", currentTweet.inReplyToStatusId);
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
- logger6.log("Found parent tweet:", {
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
- logger6.log(
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
- logger6.log("Error fetching parent tweet:", {
8396
+ logger7.log("Error fetching parent tweet:", {
8890
8397
  tweetId: currentTweet.inReplyToStatusId,
8891
8398
  error
8892
8399
  });
8893
8400
  }
8894
8401
  } else {
8895
- logger6.log("Reached end of reply chain at:", currentTweet.id);
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 ChannelType5,
8906
- ModelTypes as ModelTypes5,
8907
- cleanJsonResponse,
8908
- composePrompt as composePrompt3,
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
- logger7.log("Twitter Client Configuration:");
8943
- logger7.log(`- Username: ${this.twitterUsername}`);
8944
- logger7.log(`- Dry Run Mode: ${this.isDryRun ? "enabled" : "disabled"}`);
8945
- logger7.log(
8946
- `- Disable Post: ${this.state?.TWITTER_ENABLE_POST_GENERATION || this.runtime.getSetting("TWITTER_ENABLE_POST_GENERATION") ? "disabled" : "enabled"}`
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
- logger7.log(
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
- logger7.log(
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
- logger7.log(
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
- * Asynchronously starts the tweet generation loop.
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
- if (!this.client.profile) {
8968
- await this.client.init();
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
- let lastPost = await this.runtime.getDatabaseAdapter().getCache(`twitter/${this.twitterUsername}/lastPost`);
8972
- if (!lastPost) {
8973
- lastPost = JSON.stringify({
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
- if (this.state?.TWITTER_ENABLE_POST_GENERATION || this.runtime.getSetting("TWITTER_ENABLE_POST_GENERATION")) {
8991
- if (this.state?.TWITTER_POST_IMMEDIATELY || this.runtime.getSetting("TWITTER_POST_IMMEDIATELY")) {
8992
- setTimeout(() => {
8993
- this.generateNewTweet();
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.getDatabaseAdapter().setCache(`twitter/${client.profile.username}/lastPost`, {
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
- logger7.log(`Tweet posted:
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: ChannelType5.FEED
8530
+ type: ChannelType6.FEED
9050
8531
  });
9051
8532
  await runtime.ensureParticipantInRoom(runtime.agentId, roomId);
9052
- await runtime.getMemoryManager("messages").createMemory({
9053
- id: createUniqueUuid5(this.runtime, tweet.id),
9054
- entityId: runtime.agentId,
9055
- agentId: runtime.agentId,
9056
- content: {
9057
- text: rawTweetContent.trim(),
9058
- url: tweet.permanentUrl,
9059
- source: "twitter"
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
- roomId,
9062
- createdAt: tweet.timestamp
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
- logger7.error("Error sending tweet; Bad response:", body);
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
- logger7.error("Error sending standard Tweet:", error);
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
- logger7.log("Posting new tweet:\n");
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
- logger7.error("Error sending tweet:");
8622
+ logger8.error("Error sending tweet:");
9155
8623
  throw error;
9156
8624
  }
9157
8625
  }
9158
8626
  /**
9159
- * Generates and posts a new tweet. If isDryRun is true, only logs what would have been posted.
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 roomId = createUniqueUuid5(this.runtime, "twitter_generate");
9171
- const topics = this.runtime.character.topics.sort(() => 0.5 - Math.random()).slice(0, 10).join(", ");
9172
- const state = await this.runtime.composeState({
9173
- entityId: this.runtime.agentId,
9174
- roomId,
9175
- agentId: this.runtime.agentId,
9176
- content: {
9177
- text: topics || "",
9178
- actions: ["TWEET"]
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
- const prompt = composePrompt3({
9186
- state,
9187
- template: this.runtime.character.templates?.twitterPostTemplate || twitterPostTemplate
9188
- });
9189
- logger7.debug(`generate post prompt:
9190
- ${prompt}`);
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
- const rawTweetContent = cleanJsonResponse(response);
9195
- let tweetTextForPosting = null;
9196
- let mediaData = null;
9197
- const parsedResponse = parseJSONObjectFromText2(rawTweetContent);
9198
- if (parsedResponse?.text) {
9199
- tweetTextForPosting = parsedResponse.text;
9200
- } else {
9201
- tweetTextForPosting = rawTweetContent.trim();
9202
- }
9203
- if (parsedResponse?.attachments && parsedResponse?.attachments.length > 0) {
9204
- mediaData = await fetchMediaData(parsedResponse.attachments);
9205
- }
9206
- if (!tweetTextForPosting) {
9207
- const parsingText = extractAttributes(rawTweetContent, ["text"]).text;
9208
- if (parsingText) {
9209
- tweetTextForPosting = truncateToCompleteSentence(
9210
- extractAttributes(rawTweetContent, ["text"]).text,
9211
- 280 - 1
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
- if (!tweetTextForPosting) {
9216
- tweetTextForPosting = rawTweetContent;
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 removeQuotes = (str) => str.replace(/^['"](.*)['"]$/, "$1");
9223
- const fixNewLines = (str) => str.replaceAll(/\\n/g, "\n\n");
9224
- tweetTextForPosting = removeQuotes(fixNewLines(tweetTextForPosting));
9225
- if (this.isDryRun) {
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
- logger7.error("Error generating new tweet:", error);
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
- ModelTypes as ModelTypes6,
9255
- createUniqueUuid as createUniqueUuid6,
9256
- logger as logger8,
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(ServiceTypes2.TWITTER);
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
- logger8.success("TwitterClient initialized successfully.");
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
- logger8.log("Successfully fetched Twitter profile:", profile);
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
- console.log(
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
- logger8.log(
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
- logger8.log(`Successfully fetched ${posts.length} own posts.`);
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 = createUniqueUuid6(runtime, "twitter_mock_room");
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
- logger8.success("Successfully posted a test tweet.");
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 = createUniqueUuid6(runtime, "twitter_mock_room");
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
- logger8.success("Successfully posted a test tweet.");
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
- logger8.success("Successfully generated a new tweet.");
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: createUniqueUuid6(runtime, testTweet.entityId),
9512
- roomId: createUniqueUuid6(runtime, testTweet.conversationId)
8964
+ entityId: createUniqueUuid7(runtime, testTweet.entityId),
8965
+ roomId: createUniqueUuid7(runtime, testTweet.conversationId)
9513
8966
  },
9514
8967
  thread: []
9515
8968
  });
9516
- logger8.success("Correct response decision.");
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(ModelTypes6.TEXT_SMALL, {
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 TwitterService = class _TwitterService extends Service {
9576
- static serviceType = TWITTER_SERVICE_NAME;
9577
- capabilityDescription = "The agent is able to send and receive messages on twitter";
9578
- static instance;
9579
- clients = /* @__PURE__ */ new Map();
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
- logger9.info(`Twitter client already exists for ${clientId}`);
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
- logger9.info(`Created Twitter client for ${clientId}`);
9053
+ await this.emitServerJoinedEvent(runtime, client);
9054
+ logger10.info(`Created Twitter client for ${clientId}`);
9610
9055
  return client;
9611
9056
  } catch (error) {
9612
- logger9.error(`Failed to create Twitter client for ${clientId}:`, error);
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
- logger9.info(`Stopped Twitter client for ${clientId}`);
9148
+ logger10.info(`Stopped Twitter client for ${clientId}`);
9627
9149
  } catch (error) {
9628
- logger9.error(`Error stopping Twitter client for ${clientId}:`, error);
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
- logger9.info("Creating default Twitter client from character settings");
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
- logger9.error("Failed to create default Twitter client:", error);
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
- logger9.error(`Error stopping Twitter client ${key}:`, error);
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",