@elizaos/plugin-twitter 1.0.0-beta.41 → 1.0.0-beta.43

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,11 +1,11 @@
1
1
  // src/index.ts
2
2
  import {
3
- ChannelType as ChannelType7,
3
+ ChannelType as ChannelType8,
4
4
  EventType as EventType4,
5
5
  Role,
6
6
  Service,
7
- createUniqueUuid as createUniqueUuid7,
8
- logger as logger10
7
+ createUniqueUuid as createUniqueUuid8,
8
+ logger as logger11
9
9
  } from "@elizaos/core";
10
10
 
11
11
  // src/actions/spaceJoin.ts
@@ -4604,29 +4604,29 @@ async function unmuteSpeaker(params) {
4604
4604
  throw new Error(`unmuteSpeaker => ${resp.status} ${text}`);
4605
4605
  }
4606
4606
  }
4607
- function setupCommonChatEvents(chatClient, logger11, emitter) {
4607
+ function setupCommonChatEvents(chatClient, logger12, emitter) {
4608
4608
  chatClient.on("occupancyUpdate", (upd) => {
4609
- logger11.debug("[ChatEvents] occupancyUpdate =>", upd);
4609
+ logger12.debug("[ChatEvents] occupancyUpdate =>", upd);
4610
4610
  emitter.emit("occupancyUpdate", upd);
4611
4611
  });
4612
4612
  chatClient.on("guestReaction", (reaction) => {
4613
- logger11.debug("[ChatEvents] guestReaction =>", reaction);
4613
+ logger12.debug("[ChatEvents] guestReaction =>", reaction);
4614
4614
  emitter.emit("guestReaction", reaction);
4615
4615
  });
4616
4616
  chatClient.on("muteStateChanged", (evt) => {
4617
- logger11.debug("[ChatEvents] muteStateChanged =>", evt);
4617
+ logger12.debug("[ChatEvents] muteStateChanged =>", evt);
4618
4618
  emitter.emit("muteStateChanged", evt);
4619
4619
  });
4620
4620
  chatClient.on("speakerRequest", (req) => {
4621
- logger11.debug("[ChatEvents] speakerRequest =>", req);
4621
+ logger12.debug("[ChatEvents] speakerRequest =>", req);
4622
4622
  emitter.emit("speakerRequest", req);
4623
4623
  });
4624
4624
  chatClient.on("newSpeakerAccepted", (info) => {
4625
- logger11.debug("[ChatEvents] newSpeakerAccepted =>", info);
4625
+ logger12.debug("[ChatEvents] newSpeakerAccepted =>", info);
4626
4626
  emitter.emit("newSpeakerAccepted", info);
4627
4627
  });
4628
4628
  chatClient.on("newSpeakerRemoved", (info) => {
4629
- logger11.debug("[ChatEvents] newSpeakerRemoved =>", info);
4629
+ logger12.debug("[ChatEvents] newSpeakerRemoved =>", info);
4630
4630
  emitter.emit("newSpeakerRemoved", info);
4631
4631
  });
4632
4632
  }
@@ -6699,6 +6699,31 @@ async function sendTweet(client, text, mediaData = [], tweetToReplyTo) {
6699
6699
  return await sendStandardTweet(client, text, tweetToReplyTo, mediaData);
6700
6700
  }
6701
6701
  }
6702
+ var parseActionResponseFromText = (text) => {
6703
+ const actions = {
6704
+ like: false,
6705
+ retweet: false,
6706
+ quote: false,
6707
+ reply: false
6708
+ };
6709
+ const likePattern = /\[LIKE\]/i;
6710
+ const retweetPattern = /\[RETWEET\]/i;
6711
+ const quotePattern = /\[QUOTE\]/i;
6712
+ const replyPattern = /\[REPLY\]/i;
6713
+ actions.like = likePattern.test(text);
6714
+ actions.retweet = retweetPattern.test(text);
6715
+ actions.quote = quotePattern.test(text);
6716
+ actions.reply = replyPattern.test(text);
6717
+ const lines = text.split("\n");
6718
+ for (const line of lines) {
6719
+ const trimmed = line.trim();
6720
+ if (trimmed === "[LIKE]") actions.like = true;
6721
+ if (trimmed === "[RETWEET]") actions.retweet = true;
6722
+ if (trimmed === "[QUOTE]") actions.quote = true;
6723
+ if (trimmed === "[REPLY]") actions.reply = true;
6724
+ }
6725
+ return { actions };
6726
+ };
6702
6727
  async function generateFiller(runtime, fillerType) {
6703
6728
  try {
6704
6729
  const prompt = composePrompt({
@@ -8428,8 +8453,7 @@ import {
8428
8453
  ChannelType as ChannelType6,
8429
8454
  EventType as EventType3,
8430
8455
  createUniqueUuid as createUniqueUuid6,
8431
- logger as logger8,
8432
- parseBooleanFromText
8456
+ logger as logger8
8433
8457
  } from "@elizaos/core";
8434
8458
  var TwitterPostClient = class {
8435
8459
  /**
@@ -8447,12 +8471,6 @@ var TwitterPostClient = class {
8447
8471
  logger8.log("Twitter Client Configuration:");
8448
8472
  logger8.log(`- Username: ${this.twitterUsername}`);
8449
8473
  logger8.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
8450
- this.state.isTwitterEnabled = parseBooleanFromText(
8451
- String(
8452
- this.state?.TWITTER_ENABLE_POST_GENERATION || this.runtime.getSetting("TWITTER_ENABLE_POST_GENERATION") || ""
8453
- )
8454
- );
8455
- logger8.log(`- Auto-post: ${this.state.isTwitterEnabled ? "enabled" : "disabled"}`);
8456
8474
  logger8.log(
8457
8475
  `- Post Interval: ${this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN") || 90}-${this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX") || 180} minutes`
8458
8476
  );
@@ -8468,11 +8486,6 @@ var TwitterPostClient = class {
8468
8486
  */
8469
8487
  async start() {
8470
8488
  logger8.log("Starting Twitter post client...");
8471
- const tweetGeneration = this.state.isTwitterEnabled;
8472
- if (tweetGeneration === false) {
8473
- logger8.log("Tweet generation is disabled");
8474
- return;
8475
- }
8476
8489
  const generateNewTweetLoop = async () => {
8477
8490
  const minPostMinutes = this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN") || 90;
8478
8491
  const maxPostMinutes = this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX") || 180;
@@ -8599,8 +8612,370 @@ var TwitterPostClient = class {
8599
8612
  }
8600
8613
  };
8601
8614
 
8602
- // src/tests.ts
8615
+ // src/timeline.ts
8616
+ import {
8617
+ ChannelType as ChannelType7,
8618
+ composePromptFromState,
8619
+ createUniqueUuid as createUniqueUuid7,
8620
+ ModelType as ModelType5,
8621
+ parseKeyValueXml
8622
+ } from "@elizaos/core";
8603
8623
  import { logger as logger9 } from "@elizaos/core";
8624
+
8625
+ // src/templates.ts
8626
+ var twitterActionTemplate = `
8627
+ # INSTRUCTIONS: Determine actions for {{agentName}} (@{{twitterUserName}}) based on:
8628
+ {{bio}}
8629
+ {{postDirections}}
8630
+
8631
+ Guidelines:
8632
+ - ONLY engage with content that DIRECTLY relates to character's core interests
8633
+ - Direct mentions are priority IF they are on-topic
8634
+ - Skip ALL content that is:
8635
+ - Off-topic or tangentially related
8636
+ - From high-profile accounts unless explicitly relevant
8637
+ - Generic/viral content without specific relevance
8638
+ - Political/controversial unless central to character
8639
+ - Promotional/marketing unless directly relevant
8640
+
8641
+ Actions (respond only with tags):
8642
+ [LIKE] - Perfect topic match AND aligns with character (9.8/10)
8643
+ [RETWEET] - Exceptional content that embodies character's expertise (9.5/10)
8644
+ [QUOTE] - Can add substantial domain expertise (9.5/10)
8645
+ [REPLY] - Can contribute meaningful, expert-level insight (9.5/10)
8646
+ `;
8647
+ var quoteTweetTemplate = `# Task: Write a quote tweet in the voice, style, and perspective of {{agentName}} @{{twitterUserName}}.
8648
+
8649
+ {{bio}}
8650
+ {{postDirections}}
8651
+
8652
+ <response>
8653
+ <thought>Your thought here, explaining why the quote tweet is meaningful or how it connects to what {{agentName}} cares about</thought>
8654
+ <post>The quote tweet content here, under 280 characters, without emojis, no questions</post>
8655
+ </response>
8656
+
8657
+ Your quote tweet should be:
8658
+ - A reaction, agreement, disagreement, or expansion of the original tweet
8659
+ - Personal and unique to {{agentName}}\u2019s style and point of view
8660
+ - 1 to 3 sentences long, chosen at random
8661
+ - No questions, no emojis, concise
8662
+ - Use "\\n\\n" (double spaces) between multiple sentences
8663
+ - Max 280 characters including line breaks
8664
+
8665
+ Your output must ONLY contain the XML block.`;
8666
+ var replyTweetTemplate = `# Task: Write a reply tweet in the voice, style, and perspective of {{agentName}} @{{twitterUserName}}.
8667
+
8668
+ {{bio}}
8669
+ {{postDirections}}
8670
+
8671
+ <response>
8672
+ <thought>Your thought here, explaining why this reply is meaningful or how it connects to what {{agentName}} cares about</thought>
8673
+ <post>The reply tweet content here, under 280 characters, without emojis, no questions</post>
8674
+ </response>
8675
+
8676
+ Your reply should be:
8677
+ - A direct response, agreement, disagreement, or personal take on the original tweet
8678
+ - Reflective of {{agentName}}\u2019s unique voice and values
8679
+ - 1 to 2 sentences long, chosen at random
8680
+ - No questions, no emojis, concise
8681
+ - Use "\\n\\n" (double spaces) between multiple sentences if needed
8682
+ - Max 280 characters including line breaks
8683
+
8684
+ Your output must ONLY contain the XML block.`;
8685
+
8686
+ // src/timeline.ts
8687
+ var TwitterTimelineClient = class {
8688
+ constructor(client, runtime, state) {
8689
+ this.client = client;
8690
+ this.twitterClient = client.twitterClient;
8691
+ this.runtime = runtime;
8692
+ this.state = state;
8693
+ this.timelineType = this.state?.TWITTER_TIMELINE_MODE || this.runtime.getSetting("TWITTER_TIMELINE_MODE");
8694
+ }
8695
+ async start() {
8696
+ const handleTwitterTimelineLoop = () => {
8697
+ const interactionInterval = (this.state?.TWITTER_TIMELINE_POLL_INTERVAL || this.runtime.getSetting("TWITTER_TIMELINE_POLL_INTERVAL") || 120) * 1e3;
8698
+ this.handleTimeline();
8699
+ setTimeout(handleTwitterTimelineLoop, interactionInterval);
8700
+ };
8701
+ handleTwitterTimelineLoop();
8702
+ }
8703
+ async getTimeline(count) {
8704
+ const twitterUsername = this.client.profile?.username;
8705
+ const homeTimeline = this.timelineType === "following" /* Following */ ? await this.twitterClient.fetchFollowingTimeline(count, []) : await this.twitterClient.fetchHomeTimeline(count, []);
8706
+ return homeTimeline.map((tweet) => ({
8707
+ id: tweet.rest_id,
8708
+ name: tweet.core?.user_results?.result?.legacy?.name,
8709
+ username: tweet.core?.user_results?.result?.legacy?.screen_name,
8710
+ text: tweet.legacy?.full_text,
8711
+ inReplyToStatusId: tweet.legacy?.in_reply_to_status_id_str,
8712
+ timestamp: new Date(tweet.legacy?.created_at).getTime() / 1e3,
8713
+ userId: tweet.legacy?.user_id_str,
8714
+ conversationId: tweet.legacy?.conversation_id_str,
8715
+ permanentUrl: `https://twitter.com/${tweet.core?.user_results?.result?.legacy?.screen_name}/status/${tweet.rest_id}`,
8716
+ hashtags: tweet.legacy?.entities?.hashtags || [],
8717
+ mentions: tweet.legacy?.entities?.user_mentions || [],
8718
+ photos: tweet.legacy?.entities?.media?.filter((media) => media.type === "photo").map((media) => ({
8719
+ id: media.id_str,
8720
+ url: media.media_url_https,
8721
+ // Store media_url_https as url
8722
+ alt_text: media.alt_text
8723
+ })) || [],
8724
+ thread: tweet.thread || [],
8725
+ urls: tweet.legacy?.entities?.urls || [],
8726
+ videos: tweet.legacy?.entities?.media?.filter((media) => media.type === "video") || []
8727
+ })).filter((tweet) => tweet.username !== twitterUsername);
8728
+ }
8729
+ createTweetId(runtime, tweet) {
8730
+ return createUniqueUuid7(runtime, tweet.id);
8731
+ }
8732
+ formMessage(runtime, tweet) {
8733
+ return {
8734
+ id: this.createTweetId(runtime, tweet),
8735
+ agentId: runtime.agentId,
8736
+ content: {
8737
+ text: tweet.text,
8738
+ url: tweet.permanentUrl,
8739
+ imageUrls: tweet.photos?.map((photo) => photo.url) || [],
8740
+ inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid7(runtime, tweet.inReplyToStatusId) : void 0,
8741
+ source: "twitter",
8742
+ channelType: ChannelType7.GROUP,
8743
+ tweet
8744
+ },
8745
+ entityId: createUniqueUuid7(runtime, tweet.userId),
8746
+ roomId: createUniqueUuid7(runtime, tweet.conversationId),
8747
+ createdAt: tweet.timestamp * 1e3
8748
+ };
8749
+ }
8750
+ async handleTimeline() {
8751
+ console.log("Start Hanldeling Twitter Timeline");
8752
+ const tweets = await this.getTimeline(20);
8753
+ const maxActionsPerCycle = 20;
8754
+ const tweetDecisions = [];
8755
+ for (const tweet of tweets) {
8756
+ try {
8757
+ const tweetId = this.createTweetId(this.runtime, tweet);
8758
+ const memory = await this.runtime.getMemoryById(tweetId);
8759
+ if (memory) {
8760
+ console.log(`Already processed tweet ID: ${tweet.id}`);
8761
+ continue;
8762
+ }
8763
+ const roomId = createUniqueUuid7(this.runtime, tweet.conversationId);
8764
+ const message = this.formMessage(this.runtime, tweet);
8765
+ let state = await this.runtime.composeState(message);
8766
+ const actionRespondPrompt = composePromptFromState({
8767
+ state,
8768
+ template: this.runtime.character.templates?.twitterActionTemplate || twitterActionTemplate
8769
+ }) + `
8770
+ Tweet:
8771
+ ${tweet.text}
8772
+
8773
+ # Respond with qualifying action tags only.
8774
+
8775
+ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appropriate. Each action must be on its own line. Your response must only include the chosen actions.`;
8776
+ const actionResponse = await this.runtime.useModel(ModelType5.TEXT_SMALL, {
8777
+ prompt: actionRespondPrompt
8778
+ });
8779
+ if (!actionResponse) {
8780
+ logger9.log(`No valid actions generated for tweet ${tweet.id}`);
8781
+ continue;
8782
+ }
8783
+ const { actions } = parseActionResponseFromText(actionResponse.trim());
8784
+ tweetDecisions.push({
8785
+ tweet,
8786
+ actionResponse: actions,
8787
+ tweetState: state,
8788
+ roomId
8789
+ });
8790
+ } catch (error) {
8791
+ logger9.error(`Error processing tweet ${tweet.id}:`, error);
8792
+ continue;
8793
+ }
8794
+ }
8795
+ const rankByActionRelevance = (arr) => {
8796
+ return arr.sort((a, b) => {
8797
+ const countTrue = (obj) => Object.values(obj).filter(Boolean).length;
8798
+ const countA = countTrue(a.actionResponse);
8799
+ const countB = countTrue(b.actionResponse);
8800
+ if (countA !== countB) {
8801
+ return countB - countA;
8802
+ }
8803
+ if (a.actionResponse.like !== b.actionResponse.like) {
8804
+ return a.actionResponse.like ? -1 : 1;
8805
+ }
8806
+ return 0;
8807
+ });
8808
+ };
8809
+ const prioritizedTweets = rankByActionRelevance(tweetDecisions);
8810
+ this.processTimelineActions(prioritizedTweets);
8811
+ }
8812
+ async processTimelineActions(tweetDecisions) {
8813
+ const results = [];
8814
+ for (const decision of tweetDecisions) {
8815
+ const { actionResponse, tweetState, roomId, tweet } = decision;
8816
+ const entityId = createUniqueUuid7(this.runtime, tweet.userId);
8817
+ const worldId = createUniqueUuid7(this.runtime, tweet.userId);
8818
+ await this.ensureTweetWorldContext(tweet, roomId, worldId, entityId);
8819
+ try {
8820
+ const message = this.formMessage(this.runtime, tweet);
8821
+ await Promise.all([
8822
+ this.runtime.addEmbeddingToMemory(message),
8823
+ this.runtime.createMemory(message, "messages")
8824
+ ]);
8825
+ if (actionResponse.like) {
8826
+ this.handleLikeAction(tweet);
8827
+ }
8828
+ if (actionResponse.retweet) {
8829
+ this.handleRetweetAction(tweet);
8830
+ }
8831
+ if (actionResponse.quote) {
8832
+ this.handleQuoteAction(tweet);
8833
+ }
8834
+ if (actionResponse.reply) {
8835
+ this.handleReplyAction(tweet);
8836
+ }
8837
+ } catch (error) {
8838
+ logger9.error(`Error processing tweet ${tweet.id}:`, error);
8839
+ continue;
8840
+ }
8841
+ }
8842
+ return results;
8843
+ }
8844
+ async ensureTweetWorldContext(tweet, roomId, worldId, entityId) {
8845
+ await this.runtime.ensureWorldExists({
8846
+ id: worldId,
8847
+ name: `${tweet.name}'s Twitter`,
8848
+ agentId: this.runtime.agentId,
8849
+ serverId: tweet.userId,
8850
+ metadata: {
8851
+ ownership: { ownerId: tweet.userId },
8852
+ twitter: {
8853
+ username: tweet.username,
8854
+ id: tweet.userId,
8855
+ name: tweet.name
8856
+ }
8857
+ }
8858
+ });
8859
+ await this.runtime.ensureConnection({
8860
+ entityId,
8861
+ roomId,
8862
+ userName: tweet.username,
8863
+ name: tweet.name,
8864
+ source: "twitter",
8865
+ type: ChannelType7.GROUP,
8866
+ channelId: tweet.conversationId,
8867
+ serverId: tweet.userId,
8868
+ worldId
8869
+ });
8870
+ await this.runtime.ensureRoomExists({
8871
+ id: roomId,
8872
+ name: `Conversation with ${tweet.name}`,
8873
+ source: "twitter",
8874
+ type: ChannelType7.GROUP,
8875
+ channelId: tweet.conversationId,
8876
+ serverId: tweet.userId,
8877
+ worldId
8878
+ });
8879
+ }
8880
+ async handleLikeAction(tweet) {
8881
+ try {
8882
+ await this.twitterClient.likeTweet(tweet.id);
8883
+ logger9.log(`Liked tweet ${tweet.id}`);
8884
+ } catch (error) {
8885
+ logger9.error(`Error liking tweet ${tweet.id}:`, error);
8886
+ }
8887
+ }
8888
+ async handleRetweetAction(tweet) {
8889
+ try {
8890
+ await this.twitterClient.retweet(tweet.id);
8891
+ logger9.log(`Retweeted tweet ${tweet.id}`);
8892
+ } catch (error) {
8893
+ logger9.error(`Error retweeting tweet ${tweet.id}:`, error);
8894
+ }
8895
+ }
8896
+ async handleQuoteAction(tweet) {
8897
+ try {
8898
+ const message = this.formMessage(this.runtime, tweet);
8899
+ let state = await this.runtime.composeState(message);
8900
+ const quotePrompt = composePromptFromState({
8901
+ state,
8902
+ template: this.runtime.character.templates?.quoteTweetTemplate || quoteTweetTemplate
8903
+ }) + `
8904
+ You are responding to this tweet:
8905
+ ${tweet.text}`;
8906
+ const quoteResponse = await this.runtime.useModel(ModelType5.TEXT_SMALL, {
8907
+ prompt: quotePrompt
8908
+ });
8909
+ const responseObject = parseKeyValueXml(quoteResponse);
8910
+ if (responseObject.post) {
8911
+ const result = await this.client.requestQueue.add(
8912
+ async () => await this.twitterClient.sendQuoteTweet(responseObject.post, tweet.id)
8913
+ );
8914
+ const body = await result.json();
8915
+ if (body?.data?.create_tweet?.tweet_results?.result) {
8916
+ logger9.log("Successfully posted quote tweet");
8917
+ } else {
8918
+ logger9.error("Quote tweet creation failed:", body);
8919
+ }
8920
+ const responseId = createUniqueUuid7(this.runtime, body.rest_id);
8921
+ const responseMemory = {
8922
+ id: responseId,
8923
+ entityId: this.runtime.agentId,
8924
+ agentId: this.runtime.agentId,
8925
+ roomId: message.roomId,
8926
+ content: {
8927
+ ...responseObject,
8928
+ inReplyTo: message.id
8929
+ },
8930
+ createdAt: Date.now()
8931
+ };
8932
+ await this.runtime.createMemory(responseMemory, "messages");
8933
+ }
8934
+ } catch (error) {
8935
+ logger9.error("Error in quote tweet generation:", error);
8936
+ }
8937
+ }
8938
+ async handleReplyAction(tweet) {
8939
+ try {
8940
+ const message = this.formMessage(this.runtime, tweet);
8941
+ let state = await this.runtime.composeState(message);
8942
+ const replyPrompt = composePromptFromState({
8943
+ state,
8944
+ template: this.runtime.character.templates?.replyTweetTemplate || replyTweetTemplate
8945
+ }) + `
8946
+ You are responding to this tweet:
8947
+ ${tweet.text}`;
8948
+ const replyResponse = await this.runtime.useModel(ModelType5.TEXT_SMALL, {
8949
+ prompt: replyPrompt
8950
+ });
8951
+ const responseObject = parseKeyValueXml(replyResponse);
8952
+ if (responseObject.post) {
8953
+ const tweetResult = await sendTweet(this.client, responseObject.post, [], tweet.id);
8954
+ if (!tweetResult) {
8955
+ throw new Error("Failed to get tweet result from response");
8956
+ }
8957
+ const responseId = createUniqueUuid7(this.runtime, tweetResult.rest_id);
8958
+ const responseMemory = {
8959
+ id: responseId,
8960
+ entityId: this.runtime.agentId,
8961
+ agentId: this.runtime.agentId,
8962
+ roomId: message.roomId,
8963
+ content: {
8964
+ ...responseObject,
8965
+ inReplyTo: message.id
8966
+ },
8967
+ createdAt: Date.now()
8968
+ };
8969
+ await this.runtime.createMemory(responseMemory, "messages");
8970
+ }
8971
+ } catch (error) {
8972
+ logger9.error("Error in quote tweet generation:", error);
8973
+ }
8974
+ }
8975
+ };
8976
+
8977
+ // src/tests.ts
8978
+ import { logger as logger10 } from "@elizaos/core";
8604
8979
  var ClientBaseTestSuite = class {
8605
8980
  constructor() {
8606
8981
  this.name = "twitter-client-base";
@@ -8660,7 +9035,7 @@ var ClientBaseTestSuite = class {
8660
9035
  if (client.state.TWITTER_DRY_RUN !== true) {
8661
9036
  throw new Error("Client state TWITTER_DRY_RUN mismatch.");
8662
9037
  }
8663
- logger9.success("ClientBase instance created with correct configuration.");
9038
+ logger10.success("ClientBase instance created with correct configuration.");
8664
9039
  }
8665
9040
  async testPostIntervals() {
8666
9041
  const client = new ClientBase(this.mockRuntime, this.mockConfig);
@@ -8676,7 +9051,7 @@ var ClientBaseTestSuite = class {
8676
9051
  if (client.state.TWITTER_POST_INTERVAL_MAX !== 180) {
8677
9052
  throw new Error("Client state TWITTER_POST_INTERVAL_MAX mismatch.");
8678
9053
  }
8679
- logger9.success("ClientBase initialized with correct post intervals.");
9054
+ logger10.success("ClientBase initialized with correct post intervals.");
8680
9055
  }
8681
9056
  };
8682
9057
 
@@ -8684,8 +9059,15 @@ var ClientBaseTestSuite = class {
8684
9059
  var TwitterClientInstance = class {
8685
9060
  constructor(runtime, state) {
8686
9061
  this.client = new ClientBase(runtime, state);
8687
- this.post = new TwitterPostClient(this.client, runtime, state);
8688
- this.interaction = new TwitterInteractionClient(this.client, runtime, state);
9062
+ if (runtime.getSetting("TWITTER_ENABLE_POST_GENERATION") === true) {
9063
+ this.post = new TwitterPostClient(this.client, runtime, state);
9064
+ }
9065
+ if (runtime.getSetting("TWITTER_INTERACTION_ENABLE") === true) {
9066
+ this.interaction = new TwitterInteractionClient(this.client, runtime, state);
9067
+ }
9068
+ if (runtime.getSetting("TWITTER_TIMELINE_ENABLE") === true) {
9069
+ this.timeline = new TwitterTimelineClient(this.client, runtime, state);
9070
+ }
8689
9071
  if (runtime.getSetting("TWITTER_SPACES_ENABLE") === true) {
8690
9072
  this.space = new TwitterSpaceClient(this.client, runtime);
8691
9073
  }
@@ -8711,7 +9093,7 @@ var _TwitterService = class _TwitterService extends Service {
8711
9093
  try {
8712
9094
  const existingClient = this.getClient(clientId, runtime.agentId);
8713
9095
  if (existingClient) {
8714
- logger10.info(`Twitter client already exists for ${clientId}`);
9096
+ logger11.info(`Twitter client already exists for ${clientId}`);
8715
9097
  return existingClient;
8716
9098
  }
8717
9099
  const client = new TwitterClientInstance(runtime, state);
@@ -8725,12 +9107,15 @@ var _TwitterService = class _TwitterService extends Service {
8725
9107
  if (client.interaction) {
8726
9108
  client.interaction.start();
8727
9109
  }
9110
+ if (client.timeline) {
9111
+ client.timeline.start();
9112
+ }
8728
9113
  this.clients.set(this.getClientKey(clientId, runtime.agentId), client);
8729
9114
  await this.emitServerJoinedEvent(runtime, client);
8730
- logger10.info(`Created Twitter client for ${clientId}`);
9115
+ logger11.info(`Created Twitter client for ${clientId}`);
8731
9116
  return client;
8732
9117
  } catch (error) {
8733
- logger10.error(`Failed to create Twitter client for ${clientId}:`, error);
9118
+ logger11.error(`Failed to create Twitter client for ${clientId}:`, error);
8734
9119
  throw error;
8735
9120
  }
8736
9121
  }
@@ -8742,13 +9127,13 @@ var _TwitterService = class _TwitterService extends Service {
8742
9127
  async emitServerJoinedEvent(runtime, client) {
8743
9128
  try {
8744
9129
  if (!client.client.profile) {
8745
- logger10.warn("Twitter profile not available yet, can't emit WORLD_JOINED event");
9130
+ logger11.warn("Twitter profile not available yet, can't emit WORLD_JOINED event");
8746
9131
  return;
8747
9132
  }
8748
9133
  const profile = client.client.profile;
8749
9134
  const twitterId = profile.id;
8750
9135
  const username = profile.username;
8751
- const worldId = createUniqueUuid7(runtime, twitterId);
9136
+ const worldId = createUniqueUuid8(runtime, twitterId);
8752
9137
  const world = {
8753
9138
  id: worldId,
8754
9139
  name: `${username}'s Twitter`,
@@ -8765,27 +9150,27 @@ var _TwitterService = class _TwitterService extends Service {
8765
9150
  }
8766
9151
  }
8767
9152
  };
8768
- const homeTimelineRoomId = createUniqueUuid7(runtime, `${twitterId}-home`);
9153
+ const homeTimelineRoomId = createUniqueUuid8(runtime, `${twitterId}-home`);
8769
9154
  const homeTimelineRoom = {
8770
9155
  id: homeTimelineRoomId,
8771
9156
  name: `${username}'s Timeline`,
8772
9157
  source: "twitter",
8773
- type: ChannelType7.FEED,
9158
+ type: ChannelType8.FEED,
8774
9159
  channelId: `${twitterId}-home`,
8775
9160
  serverId: twitterId,
8776
9161
  worldId
8777
9162
  };
8778
- const mentionsRoomId = createUniqueUuid7(runtime, `${twitterId}-mentions`);
9163
+ const mentionsRoomId = createUniqueUuid8(runtime, `${twitterId}-mentions`);
8779
9164
  const mentionsRoom = {
8780
9165
  id: mentionsRoomId,
8781
9166
  name: `${username}'s Mentions`,
8782
9167
  source: "twitter",
8783
- type: ChannelType7.GROUP,
9168
+ type: ChannelType8.GROUP,
8784
9169
  channelId: `${twitterId}-mentions`,
8785
9170
  serverId: twitterId,
8786
9171
  worldId
8787
9172
  };
8788
- const twitterUserId = createUniqueUuid7(runtime, twitterId);
9173
+ const twitterUserId = createUniqueUuid8(runtime, twitterId);
8789
9174
  const twitterUser = {
8790
9175
  id: twitterUserId,
8791
9176
  names: [profile.screenName || username],
@@ -8806,9 +9191,9 @@ var _TwitterService = class _TwitterService extends Service {
8806
9191
  users: [twitterUser],
8807
9192
  source: "twitter"
8808
9193
  });
8809
- logger10.info(`Emitted WORLD_JOINED event for Twitter account ${username}`);
9194
+ logger11.info(`Emitted WORLD_JOINED event for Twitter account ${username}`);
8810
9195
  } catch (error) {
8811
- logger10.error("Failed to emit WORLD_JOINED event for Twitter:", error);
9196
+ logger11.error("Failed to emit WORLD_JOINED event for Twitter:", error);
8812
9197
  }
8813
9198
  }
8814
9199
  getClient(clientId, agentId) {
@@ -8821,9 +9206,9 @@ var _TwitterService = class _TwitterService extends Service {
8821
9206
  try {
8822
9207
  await client.service.stop();
8823
9208
  this.clients.delete(key);
8824
- logger10.info(`Stopped Twitter client for ${clientId}`);
9209
+ logger11.info(`Stopped Twitter client for ${clientId}`);
8825
9210
  } catch (error) {
8826
- logger10.error(`Error stopping Twitter client for ${clientId}:`, error);
9211
+ logger11.error(`Error stopping Twitter client for ${clientId}:`, error);
8827
9212
  }
8828
9213
  }
8829
9214
  }
@@ -8841,11 +9226,11 @@ var _TwitterService = class _TwitterService extends Service {
8841
9226
  try {
8842
9227
  if (config.TWITTER_USERNAME && // Basic auth
8843
9228
  config.TWITTER_PASSWORD && config.TWITTER_EMAIL) {
8844
- logger10.info("Creating default Twitter client from character settings");
9229
+ logger11.info("Creating default Twitter client from character settings");
8845
9230
  await twitterClientManager.createClient(runtime, runtime.agentId, config);
8846
9231
  }
8847
9232
  } catch (error) {
8848
- logger10.error("Failed to create default Twitter client:", error);
9233
+ logger11.error("Failed to create default Twitter client:", error);
8849
9234
  }
8850
9235
  return twitterClientManager;
8851
9236
  }
@@ -8858,7 +9243,7 @@ var _TwitterService = class _TwitterService extends Service {
8858
9243
  await client.service.stop();
8859
9244
  this.clients.delete(key);
8860
9245
  } catch (error) {
8861
- logger10.error(`Error stopping Twitter client ${key}:`, error);
9246
+ logger11.error(`Error stopping Twitter client ${key}:`, error);
8862
9247
  }
8863
9248
  }
8864
9249
  }