@elizaos/plugin-twitter 1.0.14 → 1.2.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/README.md CHANGED
@@ -177,11 +177,6 @@ TWITTER_POST_INTERVAL_VARIANCE=0.2 # Random variance factor for posting interval
177
177
 
178
178
  # Interaction Settings
179
179
  TWITTER_SEARCH_ENABLE=true # Enable timeline monitoring and interactions
180
- TWITTER_INTERACTION_INTERVAL_MIN=15 # Minimum interval between interactions (minutes)
181
- TWITTER_INTERACTION_INTERVAL_MAX=30 # Maximum interval between interactions (minutes)
182
- TWITTER_INTERACTION_INTERVAL_VARIANCE=0.3 # Random variance for interaction intervals
183
- TWITTER_AUTO_RESPOND_MENTIONS=true # Automatically respond to mentions
184
- TWITTER_AUTO_RESPOND_REPLIES=true # Automatically respond to replies
185
180
  TWITTER_MAX_INTERACTIONS_PER_RUN=10 # Maximum interactions processed per cycle
186
181
 
187
182
  # Timeline Algorithm Configuration
@@ -193,8 +188,8 @@ TWITTER_TIMELINE_RELEVANCE_WEIGHT=5 # Weight for relevance scoring
193
188
  # Advanced Settings
194
189
  TWITTER_MAX_TWEET_LENGTH=4000 # Maximum tweet length (for threads)
195
190
  TWITTER_DM_ONLY=false # Only interact via direct messages
196
- TWITTER_ENABLE_ACTION_PROCESSING=false # Enable timeline action processing
197
- TWITTER_ACTION_INTERVAL=240 # Action processing interval (minutes)
191
+ TWITTER_ENABLE_ACTION_PROCESSING=false # Enable timeline action processing (likes, retweets, replies)
192
+ TWITTER_ACTION_INTERVAL=30 # Timeline action processing interval in MINUTES (default: 30 minutes)
198
193
  ```
199
194
 
200
195
  ## 🎯 Common Use Cases
package/dist/index.d.ts CHANGED
@@ -769,6 +769,7 @@ declare class TwitterInteractionClient {
769
769
  runtime: IAgentRuntime;
770
770
  private isDryRun;
771
771
  private state;
772
+ private isRunning;
772
773
  /**
773
774
  * Constructor for setting up a new instance with the provided client, runtime, and state.
774
775
  * @param {ClientBase} client - The client being used for communication.
@@ -833,6 +834,7 @@ declare class TwitterInteractionClient {
833
834
  */
834
835
  buildConversationThread(tweet: Tweet$1, maxReplies?: number): Promise<Tweet$1[]>;
835
836
  private createMemoryObject;
837
+ stop(): Promise<void>;
836
838
  }
837
839
 
838
840
  /**
@@ -844,6 +846,7 @@ declare class TwitterPostClient {
844
846
  twitterUsername: string;
845
847
  private isDryRun;
846
848
  private state;
849
+ private isRunning;
847
850
  /**
848
851
  * Constructor for initializing a new Twitter client with the provided client, runtime, and state
849
852
  * @param {ClientBase} client - The client used for interacting with Twitter API
@@ -943,6 +946,8 @@ type TwitterProfile = {
943
946
  declare class RequestQueue {
944
947
  private queue;
945
948
  private processing;
949
+ private maxRetries;
950
+ private retryAttempts;
946
951
  /**
947
952
  * Asynchronously adds a request to the queue, then processes the queue.
948
953
  *
@@ -1058,6 +1063,7 @@ declare class TwitterTimelineClient {
1058
1063
  isDryRun: boolean;
1059
1064
  timelineType: TIMELINE_TYPE;
1060
1065
  private state;
1066
+ private isRunning;
1061
1067
  constructor(client: ClientBase, runtime: IAgentRuntime, state: any);
1062
1068
  start(): Promise<void>;
1063
1069
  getTimeline(count: number): Promise<Tweet$1[]>;
@@ -1085,6 +1091,7 @@ declare class TwitterTimelineClient {
1085
1091
  handleRetweetAction(tweet: Tweet$1): Promise<void>;
1086
1092
  handleQuoteAction(tweet: Tweet$1): Promise<void>;
1087
1093
  handleReplyAction(tweet: Tweet$1): Promise<void>;
1094
+ stop(): Promise<void>;
1088
1095
  }
1089
1096
 
1090
1097
  /**
package/dist/index.js CHANGED
@@ -1917,6 +1917,8 @@ var RequestQueue = class {
1917
1917
  constructor() {
1918
1918
  this.queue = [];
1919
1919
  this.processing = false;
1920
+ this.maxRetries = 3;
1921
+ this.retryAttempts = /* @__PURE__ */ new Map();
1920
1922
  }
1921
1923
  /**
1922
1924
  * Asynchronously adds a request to the queue, then processes the queue.
@@ -1952,14 +1954,26 @@ var RequestQueue = class {
1952
1954
  const request = this.queue.shift();
1953
1955
  try {
1954
1956
  await request();
1957
+ this.retryAttempts.delete(request);
1955
1958
  } catch (error) {
1956
- console.error("Error processing request:", error);
1957
- this.queue.unshift(request);
1958
- await this.exponentialBackoff(this.queue.length);
1959
+ logger.error("Error processing request:", error);
1960
+ const retryCount = (this.retryAttempts.get(request) || 0) + 1;
1961
+ if (retryCount < this.maxRetries) {
1962
+ this.retryAttempts.set(request, retryCount);
1963
+ this.queue.unshift(request);
1964
+ await this.exponentialBackoff(retryCount);
1965
+ break;
1966
+ } else {
1967
+ logger.error(`Max retries (${this.maxRetries}) exceeded for request, skipping`);
1968
+ this.retryAttempts.delete(request);
1969
+ }
1959
1970
  }
1960
1971
  await this.randomDelay();
1961
1972
  }
1962
1973
  this.processing = false;
1974
+ if (this.queue.length > 0) {
1975
+ this.processQueue();
1976
+ }
1963
1977
  }
1964
1978
  /**
1965
1979
  * Implements an exponential backoff strategy for retrying a task.
@@ -2006,7 +2020,7 @@ var _ClientBase = class _ClientBase {
2006
2020
  */
2007
2021
  async cacheTweet(tweet) {
2008
2022
  if (!tweet) {
2009
- console.warn("Tweet is undefined, skipping cache");
2023
+ logger.warn("Tweet is undefined, skipping cache");
2010
2024
  return;
2011
2025
  }
2012
2026
  this.runtime.setCache(`twitter/tweets/${tweet.id}`, tweet);
@@ -2420,7 +2434,7 @@ var _ClientBase = class _ClientBase {
2420
2434
  });
2421
2435
  return profile;
2422
2436
  } catch (error) {
2423
- console.error("Error fetching Twitter profile:", error);
2437
+ logger.error("Error fetching Twitter profile:", error);
2424
2438
  throw error;
2425
2439
  }
2426
2440
  }
@@ -6666,6 +6680,7 @@ var TwitterInteractionClient = class {
6666
6680
  * @param {any} state - The initial state of the agent.
6667
6681
  */
6668
6682
  constructor(client, runtime, state) {
6683
+ this.isRunning = false;
6669
6684
  this.client = client;
6670
6685
  this.runtime = runtime;
6671
6686
  this.state = state;
@@ -6676,12 +6691,19 @@ var TwitterInteractionClient = class {
6676
6691
  * Uses an interval based on the 'TWITTER_POLL_INTERVAL' setting, or defaults to 2 minutes if not set.
6677
6692
  */
6678
6693
  async start() {
6694
+ this.isRunning = true;
6679
6695
  const handleTwitterInteractionsLoop = () => {
6696
+ if (!this.isRunning) {
6697
+ logger4.info("Twitter interaction client stopped, exiting loop");
6698
+ return;
6699
+ }
6680
6700
  const interactionInterval = (this.state?.TWITTER_POLL_INTERVAL || this.runtime.getSetting(
6681
6701
  "TWITTER_POLL_INTERVAL"
6682
6702
  ) || 120) * 1e3;
6683
6703
  this.handleTwitterInteractions();
6684
- setTimeout(handleTwitterInteractionsLoop, interactionInterval);
6704
+ if (this.isRunning) {
6705
+ setTimeout(handleTwitterInteractionsLoop, interactionInterval);
6706
+ }
6685
6707
  };
6686
6708
  handleTwitterInteractionsLoop();
6687
6709
  }
@@ -6745,7 +6767,12 @@ var TwitterInteractionClient = class {
6745
6767
  return shouldTarget;
6746
6768
  });
6747
6769
  }
6748
- for (const tweet of uniqueTweetCandidates) {
6770
+ const maxInteractionsPerRun = parseInt(
6771
+ this.runtime.getSetting("TWITTER_MAX_INTERACTIONS_PER_RUN") || "10"
6772
+ );
6773
+ const tweetsToProcess = uniqueTweetCandidates.slice(0, maxInteractionsPerRun);
6774
+ logger4.info(`Processing ${tweetsToProcess.length} of ${uniqueTweetCandidates.length} mention tweets (max: ${maxInteractionsPerRun})`);
6775
+ for (const tweet of tweetsToProcess) {
6749
6776
  if (!this.client.lastCheckedTweetId || BigInt(tweet.id) > this.client.lastCheckedTweetId) {
6750
6777
  const tweetId = createUniqueUuid3(this.runtime, tweet.id);
6751
6778
  const existingResponse = await this.runtime.getMemoryById(tweetId);
@@ -7149,6 +7176,10 @@ var TwitterInteractionClient = class {
7149
7176
  createdAt: Date.now()
7150
7177
  };
7151
7178
  }
7179
+ async stop() {
7180
+ logger4.info("Stopping Twitter interaction client...");
7181
+ this.isRunning = false;
7182
+ }
7152
7183
  };
7153
7184
 
7154
7185
  // src/post.ts
@@ -7166,6 +7197,7 @@ var TwitterPostClient = class {
7166
7197
  * @param {any} state - The state object containing configuration settings
7167
7198
  */
7168
7199
  constructor(client, runtime, state) {
7200
+ this.isRunning = false;
7169
7201
  this.client = client;
7170
7202
  this.state = state;
7171
7203
  this.runtime = runtime;
@@ -7192,13 +7224,20 @@ var TwitterPostClient = class {
7192
7224
  */
7193
7225
  async start() {
7194
7226
  logger5.log("Starting Twitter post client...");
7227
+ this.isRunning = true;
7195
7228
  const generateNewTweetLoop = async () => {
7229
+ if (!this.isRunning) {
7230
+ logger5.log("Twitter post client stopped, exiting loop");
7231
+ return;
7232
+ }
7196
7233
  const minPostMinutes = this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN") || 90;
7197
7234
  const maxPostMinutes = this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX") || 180;
7198
7235
  const randomMinutes = Math.floor(Math.random() * (maxPostMinutes - minPostMinutes + 1)) + minPostMinutes;
7199
7236
  const interval = randomMinutes * 60 * 1e3;
7200
7237
  await this.generateNewTweet();
7201
- setTimeout(generateNewTweetLoop, interval);
7238
+ if (this.isRunning) {
7239
+ setTimeout(generateNewTweetLoop, interval);
7240
+ }
7202
7241
  };
7203
7242
  setTimeout(generateNewTweetLoop, 60 * 1e3);
7204
7243
  const postImmediately = this.state?.TWITTER_POST_IMMEDIATELY ?? this.runtime.getSetting("TWITTER_POST_IMMEDIATELY");
@@ -7334,6 +7373,8 @@ var TwitterPostClient = class {
7334
7373
  }
7335
7374
  }
7336
7375
  async stop() {
7376
+ logger5.log("Stopping Twitter post client...");
7377
+ this.isRunning = false;
7337
7378
  }
7338
7379
  };
7339
7380
 
@@ -7411,19 +7452,31 @@ Your output must ONLY contain the XML block.`;
7411
7452
  // src/timeline.ts
7412
7453
  var TwitterTimelineClient = class {
7413
7454
  constructor(client, runtime, state) {
7455
+ this.isRunning = false;
7414
7456
  this.client = client;
7415
7457
  this.twitterClient = client.twitterClient;
7416
7458
  this.runtime = runtime;
7417
7459
  this.state = state;
7418
- this.timelineType = this.state?.TWITTER_TIMELINE_MODE || this.runtime.getSetting("TWITTER_TIMELINE_MODE");
7460
+ const dryRunSetting = this.state?.TWITTER_DRY_RUN ?? this.runtime.getSetting("TWITTER_DRY_RUN");
7461
+ this.isDryRun = dryRunSetting === true || dryRunSetting === "true" || typeof dryRunSetting === "string" && dryRunSetting.toLowerCase() === "true";
7462
+ const timelineMode = this.state?.TWITTER_TIMELINE_MODE || this.runtime.getSetting("TWITTER_TIMELINE_MODE") || "foryou";
7463
+ this.timelineType = timelineMode.toLowerCase() === "following" ? "following" /* Following */ : "foryou" /* ForYou */;
7419
7464
  }
7420
7465
  async start() {
7466
+ logger6.info("Starting Twitter timeline client...");
7467
+ this.isRunning = true;
7421
7468
  const handleTwitterTimelineLoop = () => {
7422
- const interactionInterval = (this.state?.TWITTER_TIMELINE_POLL_INTERVAL || this.runtime.getSetting(
7423
- "TWITTER_TIMELINE_POLL_INTERVAL"
7424
- ) || 120) * 1e3;
7469
+ if (!this.isRunning) {
7470
+ logger6.info("Twitter timeline client stopped, exiting loop");
7471
+ return;
7472
+ }
7473
+ const actionIntervalMinutes = this.state?.TWITTER_ACTION_INTERVAL || this.runtime.getSetting("TWITTER_ACTION_INTERVAL") || 240;
7474
+ const actionInterval = actionIntervalMinutes * 60 * 1e3;
7475
+ logger6.info(`Timeline client will check every ${actionIntervalMinutes} minutes`);
7425
7476
  this.handleTimeline();
7426
- setTimeout(handleTwitterTimelineLoop, interactionInterval);
7477
+ if (this.isRunning) {
7478
+ setTimeout(handleTwitterTimelineLoop, actionInterval);
7479
+ }
7427
7480
  };
7428
7481
  handleTwitterTimelineLoop();
7429
7482
  }
@@ -7454,8 +7507,9 @@ var TwitterTimelineClient = class {
7454
7507
  };
7455
7508
  }
7456
7509
  async handleTimeline() {
7457
- console.log("Start Hanldeling Twitter Timeline");
7510
+ logger6.info("Starting Twitter timeline processing...");
7458
7511
  const tweets = await this.getTimeline(20);
7512
+ logger6.info(`Fetched ${tweets.length} tweets from timeline`);
7459
7513
  const maxActionsPerCycle = 20;
7460
7514
  const tweetDecisions = [];
7461
7515
  for (const tweet of tweets) {
@@ -7463,7 +7517,7 @@ var TwitterTimelineClient = class {
7463
7517
  const tweetId = this.createTweetId(this.runtime, tweet);
7464
7518
  const memory = await this.runtime.getMemoryById(tweetId);
7465
7519
  if (memory) {
7466
- console.log(`Already processed tweet ID: ${tweet.id}`);
7520
+ logger6.log(`Already processed tweet ID: ${tweet.id}`);
7467
7521
  continue;
7468
7522
  }
7469
7523
  const roomId = createUniqueUuid5(this.runtime, tweet.conversationId);
@@ -7516,7 +7570,21 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7516
7570
  });
7517
7571
  };
7518
7572
  const prioritizedTweets = rankByActionRelevance(tweetDecisions);
7519
- this.processTimelineActions(prioritizedTweets);
7573
+ logger6.info(`Processing ${prioritizedTweets.length} tweets with actions`);
7574
+ if (prioritizedTweets.length > 0) {
7575
+ const actionSummary = prioritizedTweets.map((td) => {
7576
+ const actions = [];
7577
+ if (td.actionResponse.like) actions.push("LIKE");
7578
+ if (td.actionResponse.retweet) actions.push("RETWEET");
7579
+ if (td.actionResponse.quote) actions.push("QUOTE");
7580
+ if (td.actionResponse.reply) actions.push("REPLY");
7581
+ return `Tweet ${td.tweet.id}: ${actions.join(", ")}`;
7582
+ });
7583
+ logger6.info(`Actions to execute:
7584
+ ${actionSummary.join("\n")}`);
7585
+ }
7586
+ await this.processTimelineActions(prioritizedTweets);
7587
+ logger6.info("Timeline processing complete");
7520
7588
  }
7521
7589
  async processTimelineActions(tweetDecisions) {
7522
7590
  const results = [];
@@ -7574,6 +7642,10 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7574
7642
  }
7575
7643
  async handleLikeAction(tweet) {
7576
7644
  try {
7645
+ if (this.isDryRun) {
7646
+ logger6.log(`[DRY RUN] Would have liked tweet ${tweet.id}`);
7647
+ return;
7648
+ }
7577
7649
  await this.twitterClient.likeTweet(tweet.id);
7578
7650
  logger6.log(`Liked tweet ${tweet.id}`);
7579
7651
  } catch (error) {
@@ -7582,6 +7654,10 @@ Choose any combination of [LIKE], [RETWEET], [QUOTE], and [REPLY] that are appro
7582
7654
  }
7583
7655
  async handleRetweetAction(tweet) {
7584
7656
  try {
7657
+ if (this.isDryRun) {
7658
+ logger6.log(`[DRY RUN] Would have retweeted tweet ${tweet.id}`);
7659
+ return;
7660
+ }
7585
7661
  await this.twitterClient.retweet(tweet.id);
7586
7662
  logger6.log(`Retweeted tweet ${tweet.id}`);
7587
7663
  } catch (error) {
@@ -7603,6 +7679,10 @@ ${tweet.text}`;
7603
7679
  });
7604
7680
  const responseObject = parseKeyValueXml(quoteResponse);
7605
7681
  if (responseObject.post) {
7682
+ if (this.isDryRun) {
7683
+ logger6.log(`[DRY RUN] Would have quoted tweet ${tweet.id} with: ${responseObject.post}`);
7684
+ return;
7685
+ }
7606
7686
  const result = await this.client.requestQueue.add(
7607
7687
  async () => await this.twitterClient.sendQuoteTweet(
7608
7688
  responseObject.post,
@@ -7650,6 +7730,10 @@ ${tweet.text}`;
7650
7730
  });
7651
7731
  const responseObject = parseKeyValueXml(replyResponse);
7652
7732
  if (responseObject.post) {
7733
+ if (this.isDryRun) {
7734
+ logger6.log(`[DRY RUN] Would have replied to tweet ${tweet.id} with: ${responseObject.post}`);
7735
+ return;
7736
+ }
7653
7737
  const tweetResult = await sendTweet(
7654
7738
  this.client,
7655
7739
  responseObject.post,
@@ -7677,6 +7761,10 @@ ${tweet.text}`;
7677
7761
  logger6.error("Error in quote tweet generation:", error);
7678
7762
  }
7679
7763
  }
7764
+ async stop() {
7765
+ logger6.info("Stopping Twitter timeline client...");
7766
+ this.isRunning = false;
7767
+ }
7680
7768
  };
7681
7769
 
7682
7770
  // src/tests.ts
@@ -7783,7 +7871,7 @@ var ClientBaseTestSuite = class {
7783
7871
  };
7784
7872
 
7785
7873
  // src/index.ts
7786
- console.log(`Twitter plugin loaded with service name: ${TWITTER_SERVICE_NAME}`);
7874
+ logger7.info(`Twitter plugin loaded with service name: ${TWITTER_SERVICE_NAME}`);
7787
7875
  var TwitterClientInstance = class {
7788
7876
  constructor(runtime, state) {
7789
7877
  this.client = new ClientBase(runtime, state);
@@ -7842,13 +7930,13 @@ var _TwitterService = class _TwitterService extends Service {
7842
7930
  const client = new TwitterClientInstance(runtime, state);
7843
7931
  await client.client.init();
7844
7932
  if (client.post) {
7845
- client.post.start();
7933
+ await client.post.start();
7846
7934
  }
7847
7935
  if (client.interaction) {
7848
- client.interaction.start();
7936
+ await client.interaction.start();
7849
7937
  }
7850
7938
  if (client.timeline) {
7851
- client.timeline.start();
7939
+ await client.timeline.start();
7852
7940
  }
7853
7941
  this.clients.set(this.getClientKey(clientId, runtime.agentId), client);
7854
7942
  await this.emitServerJoinedEvent(runtime, client);
@@ -7955,7 +8043,15 @@ var _TwitterService = class _TwitterService extends Service {
7955
8043
  const client = this.clients.get(key);
7956
8044
  if (client) {
7957
8045
  try {
7958
- await client.service.stop();
8046
+ if (client.post) {
8047
+ await client.post.stop();
8048
+ }
8049
+ if (client.interaction) {
8050
+ await client.interaction.stop();
8051
+ }
8052
+ if (client.timeline) {
8053
+ await client.timeline.stop();
8054
+ }
7959
8055
  this.clients.delete(key);
7960
8056
  logger7.info(`Stopped Twitter client for ${clientId}`);
7961
8057
  } catch (error) {
@@ -7995,7 +8091,15 @@ var _TwitterService = class _TwitterService extends Service {
7995
8091
  async stopAllClients() {
7996
8092
  for (const [key, client] of this.clients.entries()) {
7997
8093
  try {
7998
- await client.service.stop();
8094
+ if (client.post) {
8095
+ await client.post.stop();
8096
+ }
8097
+ if (client.interaction) {
8098
+ await client.interaction.stop();
8099
+ }
8100
+ if (client.timeline) {
8101
+ await client.timeline.stop();
8102
+ }
7999
8103
  this.clients.delete(key);
8000
8104
  } catch (error) {
8001
8105
  logger7.error(`Error stopping Twitter client ${key}:`, error);