@elizaos/plugin-twitter 1.0.0-alpha.20 → 1.0.0-alpha.21
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 +788 -635
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
+
ChannelType as ChannelType6,
|
|
4
|
+
EventTypes as EventTypes3,
|
|
5
|
+
Role,
|
|
3
6
|
Service,
|
|
7
|
+
createUniqueUuid as createUniqueUuid7,
|
|
4
8
|
logger as logger9
|
|
5
9
|
} from "@elizaos/core";
|
|
6
10
|
|
|
@@ -279,12 +283,6 @@ var TwitterGuestAuth = class {
|
|
|
279
283
|
this.jar = new CookieJar();
|
|
280
284
|
this.v2Client = null;
|
|
281
285
|
}
|
|
282
|
-
bearerToken;
|
|
283
|
-
jar;
|
|
284
|
-
guestToken;
|
|
285
|
-
guestCreatedAt;
|
|
286
|
-
v2Client;
|
|
287
|
-
fetch;
|
|
288
286
|
cookieJar() {
|
|
289
287
|
return this.jar;
|
|
290
288
|
}
|
|
@@ -607,7 +605,6 @@ var TwitterUserAuthSubtask = Type.Object({
|
|
|
607
605
|
enter_text: Type.Optional(Type.Object({}))
|
|
608
606
|
});
|
|
609
607
|
var TwitterUserAuth = class extends TwitterGuestAuth {
|
|
610
|
-
userProfile;
|
|
611
608
|
async isLoggedIn() {
|
|
612
609
|
const res = await requestApi(
|
|
613
610
|
"https://api.twitter.com/1.1/account/verify_credentials.json",
|
|
@@ -2477,10 +2474,6 @@ var endpoints = {
|
|
|
2477
2474
|
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
2475
|
};
|
|
2479
2476
|
var ApiRequest = class {
|
|
2480
|
-
url;
|
|
2481
|
-
variables;
|
|
2482
|
-
features;
|
|
2483
|
-
fieldToggles;
|
|
2484
2477
|
constructor(info) {
|
|
2485
2478
|
this.url = info.url;
|
|
2486
2479
|
this.variables = info.variables;
|
|
@@ -3659,9 +3652,6 @@ var Client = class {
|
|
|
3659
3652
|
this.token = bearerToken;
|
|
3660
3653
|
this.useGuestAuth();
|
|
3661
3654
|
}
|
|
3662
|
-
auth;
|
|
3663
|
-
authTrends;
|
|
3664
|
-
token;
|
|
3665
3655
|
/**
|
|
3666
3656
|
* Initializes auth properties using a guest token.
|
|
3667
3657
|
* Used when creating a new instance of this class, and when logging out.
|
|
@@ -4398,7 +4388,6 @@ import { EventEmitter as EventEmitter4 } from "node:events";
|
|
|
4398
4388
|
|
|
4399
4389
|
// src/client/spaces/logger.ts
|
|
4400
4390
|
var Logger = class {
|
|
4401
|
-
debugEnabled;
|
|
4402
4391
|
/**
|
|
4403
4392
|
* Constructor for initializing a new instance of the class.
|
|
4404
4393
|
*
|
|
@@ -4788,14 +4777,9 @@ function setupCommonChatEvents(chatClient, logger10, emitter) {
|
|
|
4788
4777
|
import { EventEmitter } from "node:events";
|
|
4789
4778
|
import WebSocket from "ws";
|
|
4790
4779
|
var ChatClient = class extends EventEmitter {
|
|
4791
|
-
ws;
|
|
4792
|
-
connected = false;
|
|
4793
|
-
logger;
|
|
4794
|
-
spaceId;
|
|
4795
|
-
accessToken;
|
|
4796
|
-
endpoint;
|
|
4797
4780
|
constructor(config) {
|
|
4798
4781
|
super();
|
|
4782
|
+
this.connected = false;
|
|
4799
4783
|
this.spaceId = config.spaceId;
|
|
4800
4784
|
this.accessToken = config.accessToken;
|
|
4801
4785
|
this.endpoint = config.endpoint;
|
|
@@ -4994,9 +4978,6 @@ import wrtc from "@roamhq/wrtc";
|
|
|
4994
4978
|
var { nonstandard } = wrtc;
|
|
4995
4979
|
var { RTCAudioSource, RTCAudioSink } = nonstandard;
|
|
4996
4980
|
var JanusAudioSource = class extends EventEmitter2 {
|
|
4997
|
-
source;
|
|
4998
|
-
track;
|
|
4999
|
-
logger;
|
|
5000
4981
|
constructor(options) {
|
|
5001
4982
|
super();
|
|
5002
4983
|
this.logger = options?.logger;
|
|
@@ -5031,11 +5012,9 @@ var JanusAudioSource = class extends EventEmitter2 {
|
|
|
5031
5012
|
}
|
|
5032
5013
|
};
|
|
5033
5014
|
var JanusAudioSink = class extends EventEmitter2 {
|
|
5034
|
-
sink;
|
|
5035
|
-
active = true;
|
|
5036
|
-
logger;
|
|
5037
5015
|
constructor(track, options) {
|
|
5038
5016
|
super();
|
|
5017
|
+
this.active = true;
|
|
5039
5018
|
this.logger = options?.logger;
|
|
5040
5019
|
if (track.kind !== "audio") {
|
|
5041
5020
|
throw new Error("[JanusAudioSink] Provided track is not an audio track");
|
|
@@ -5069,19 +5048,13 @@ var JanusClient = class extends EventEmitter3 {
|
|
|
5069
5048
|
constructor(config) {
|
|
5070
5049
|
super();
|
|
5071
5050
|
this.config = config;
|
|
5051
|
+
this.pollActive = false;
|
|
5052
|
+
// Tracks promises waiting for specific Janus events
|
|
5053
|
+
this.eventWaiters = [];
|
|
5054
|
+
// Tracks subscriber handle+pc for each userId we subscribe to
|
|
5055
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
|
5072
5056
|
this.logger = config.logger;
|
|
5073
5057
|
}
|
|
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
5058
|
/**
|
|
5086
5059
|
* Initializes this JanusClient for the host scenario:
|
|
5087
5060
|
* 1) createSession()
|
|
@@ -5729,18 +5702,12 @@ var Space = class extends EventEmitter4 {
|
|
|
5729
5702
|
constructor(client, options) {
|
|
5730
5703
|
super();
|
|
5731
5704
|
this.client = client;
|
|
5705
|
+
this.isInitialized = false;
|
|
5706
|
+
this.plugins = /* @__PURE__ */ new Set();
|
|
5707
|
+
this.speakers = /* @__PURE__ */ new Map();
|
|
5732
5708
|
this.debug = options?.debug ?? false;
|
|
5733
5709
|
this.logger = new Logger(this.debug);
|
|
5734
5710
|
}
|
|
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
5711
|
/**
|
|
5745
5712
|
* Registers a plugin and calls its onAttach(...).
|
|
5746
5713
|
* init(...) will be invoked once initialization is complete.
|
|
@@ -6178,32 +6145,12 @@ var SpaceParticipant = class extends EventEmitter5 {
|
|
|
6178
6145
|
constructor(client, config) {
|
|
6179
6146
|
super();
|
|
6180
6147
|
this.client = client;
|
|
6148
|
+
// Plugin management
|
|
6149
|
+
this.plugins = /* @__PURE__ */ new Set();
|
|
6181
6150
|
this.spaceId = config.spaceId;
|
|
6182
6151
|
this.debug = config.debug ?? false;
|
|
6183
6152
|
this.logger = new Logger(this.debug);
|
|
6184
6153
|
}
|
|
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
6154
|
/**
|
|
6208
6155
|
* Adds a plugin and calls its onAttach immediately.
|
|
6209
6156
|
* init() or onJanusReady() will be invoked later at the appropriate time.
|
|
@@ -6501,12 +6448,9 @@ var IdleMonitorPlugin = class {
|
|
|
6501
6448
|
constructor(idleTimeoutMs = 6e4, checkEveryMs = 1e4) {
|
|
6502
6449
|
this.idleTimeoutMs = idleTimeoutMs;
|
|
6503
6450
|
this.checkEveryMs = checkEveryMs;
|
|
6451
|
+
this.lastSpeakerAudioMs = Date.now();
|
|
6452
|
+
this.lastLocalAudioMs = Date.now();
|
|
6504
6453
|
}
|
|
6505
|
-
space;
|
|
6506
|
-
logger;
|
|
6507
|
-
lastSpeakerAudioMs = Date.now();
|
|
6508
|
-
lastLocalAudioMs = Date.now();
|
|
6509
|
-
checkInterval;
|
|
6510
6454
|
/**
|
|
6511
6455
|
* Called immediately after .use(plugin).
|
|
6512
6456
|
* Allows for minimal setup, including obtaining a debug logger if desired.
|
|
@@ -6576,6 +6520,7 @@ var IdleMonitorPlugin = class {
|
|
|
6576
6520
|
import { spawn } from "node:child_process";
|
|
6577
6521
|
import {
|
|
6578
6522
|
ChannelType,
|
|
6523
|
+
EventTypes,
|
|
6579
6524
|
ModelTypes,
|
|
6580
6525
|
createUniqueUuid,
|
|
6581
6526
|
logger
|
|
@@ -6584,23 +6529,20 @@ var VOLUME_WINDOW_SIZE = 100;
|
|
|
6584
6529
|
var SPEAKING_THRESHOLD = 0.05;
|
|
6585
6530
|
var SILENCE_DETECTION_THRESHOLD_MS = 1e3;
|
|
6586
6531
|
var SttTtsPlugin2 = class {
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
userSpeakingTimer = null;
|
|
6602
|
-
volumeBuffers;
|
|
6603
|
-
ttsAbortController = null;
|
|
6532
|
+
constructor() {
|
|
6533
|
+
this.name = "SttTtsPlugin";
|
|
6534
|
+
this.description = "Speech-to-text (OpenAI) + conversation + TTS (ElevenLabs)";
|
|
6535
|
+
/**
|
|
6536
|
+
* userId => arrayOfChunks (PCM Int16)
|
|
6537
|
+
*/
|
|
6538
|
+
this.pcmBuffers = /* @__PURE__ */ new Map();
|
|
6539
|
+
// TTS queue for sequentially speaking
|
|
6540
|
+
this.ttsQueue = [];
|
|
6541
|
+
this.isSpeaking = false;
|
|
6542
|
+
this.isProcessingAudio = false;
|
|
6543
|
+
this.userSpeakingTimer = null;
|
|
6544
|
+
this.ttsAbortController = null;
|
|
6545
|
+
}
|
|
6604
6546
|
onAttach(_space) {
|
|
6605
6547
|
logger.log("[SttTtsPlugin] onAttach => space was attached");
|
|
6606
6548
|
}
|
|
@@ -6811,9 +6753,9 @@ var SttTtsPlugin2 = class {
|
|
|
6811
6753
|
`twitter_generate_room-${this.spaceId}`
|
|
6812
6754
|
);
|
|
6813
6755
|
const userUuid = createUniqueUuid(this.runtime, numericId);
|
|
6814
|
-
const entity = await this.runtime.
|
|
6756
|
+
const entity = await this.runtime.getEntityById(userUuid);
|
|
6815
6757
|
if (!entity) {
|
|
6816
|
-
await this.runtime.
|
|
6758
|
+
await this.runtime.createEntity({
|
|
6817
6759
|
id: userUuid,
|
|
6818
6760
|
names: [userId],
|
|
6819
6761
|
agentId: this.runtime.agentId
|
|
@@ -6872,7 +6814,7 @@ var SttTtsPlugin2 = class {
|
|
|
6872
6814
|
return [];
|
|
6873
6815
|
}
|
|
6874
6816
|
};
|
|
6875
|
-
this.runtime.emitEvent(
|
|
6817
|
+
this.runtime.emitEvent(EventTypes.VOICE_MESSAGE_RECEIVED, {
|
|
6876
6818
|
runtime: this.runtime,
|
|
6877
6819
|
message: memory,
|
|
6878
6820
|
callback
|
|
@@ -6985,10 +6927,6 @@ import {
|
|
|
6985
6927
|
createUniqueUuid as createUniqueUuid2,
|
|
6986
6928
|
logger as logger2
|
|
6987
6929
|
} 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
6930
|
async function buildConversationThread(tweet, client, maxReplies = 10) {
|
|
6993
6931
|
const thread = [];
|
|
6994
6932
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -7110,190 +7048,6 @@ async function fetchMediaData(attachments) {
|
|
|
7110
7048
|
})
|
|
7111
7049
|
);
|
|
7112
7050
|
}
|
|
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
7051
|
async function generateFiller(runtime, fillerType) {
|
|
7298
7052
|
try {
|
|
7299
7053
|
const prompt = composePrompt({
|
|
@@ -7366,25 +7120,15 @@ async function isAgentInSpace(client, spaceId) {
|
|
|
7366
7120
|
|
|
7367
7121
|
// src/spaces.ts
|
|
7368
7122
|
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
7123
|
constructor(client, runtime) {
|
|
7124
|
+
this.spaceStatus = "idle" /* IDLE */;
|
|
7125
|
+
this.spaceParticipant = null;
|
|
7126
|
+
this.participantStatus = "listener" /* LISTENER */;
|
|
7127
|
+
/**
|
|
7128
|
+
* We now store an array of active speakers, not just 1
|
|
7129
|
+
*/
|
|
7130
|
+
this.activeSpeakers = [];
|
|
7131
|
+
this.speakerQueue = [];
|
|
7388
7132
|
this.client = client;
|
|
7389
7133
|
this.twitterClient = client.twitterClient;
|
|
7390
7134
|
this.runtime = runtime;
|
|
@@ -7934,8 +7678,10 @@ import {
|
|
|
7934
7678
|
logger as logger5
|
|
7935
7679
|
} from "@elizaos/core";
|
|
7936
7680
|
var RequestQueue = class {
|
|
7937
|
-
|
|
7938
|
-
|
|
7681
|
+
constructor() {
|
|
7682
|
+
this.queue = [];
|
|
7683
|
+
this.processing = false;
|
|
7684
|
+
}
|
|
7939
7685
|
/**
|
|
7940
7686
|
* Asynchronously adds a request to the queue, then processes the queue.
|
|
7941
7687
|
*
|
|
@@ -7998,14 +7744,23 @@ var RequestQueue = class {
|
|
|
7998
7744
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7999
7745
|
}
|
|
8000
7746
|
};
|
|
8001
|
-
var
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
7747
|
+
var _ClientBase = class _ClientBase extends EventEmitter6 {
|
|
7748
|
+
constructor(runtime, state) {
|
|
7749
|
+
super();
|
|
7750
|
+
this.lastCheckedTweetId = null;
|
|
7751
|
+
this.temperature = 0.5;
|
|
7752
|
+
this.requestQueue = new RequestQueue();
|
|
7753
|
+
this.callback = null;
|
|
7754
|
+
this.runtime = runtime;
|
|
7755
|
+
this.state = state;
|
|
7756
|
+
const username = state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
7757
|
+
if (_ClientBase._twitterClients[username]) {
|
|
7758
|
+
this.twitterClient = _ClientBase._twitterClients[username];
|
|
7759
|
+
} else {
|
|
7760
|
+
this.twitterClient = new Client();
|
|
7761
|
+
_ClientBase._twitterClients[username] = this.twitterClient;
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
8009
7764
|
/**
|
|
8010
7765
|
* Caches a tweet in the database.
|
|
8011
7766
|
*
|
|
@@ -8017,7 +7772,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8017
7772
|
console.warn("Tweet is undefined, skipping cache");
|
|
8018
7773
|
return;
|
|
8019
7774
|
}
|
|
8020
|
-
this.runtime.
|
|
7775
|
+
this.runtime.setCache(`twitter/tweets/${tweet.id}`, tweet);
|
|
8021
7776
|
}
|
|
8022
7777
|
/**
|
|
8023
7778
|
* Retrieves a cached tweet by its ID.
|
|
@@ -8025,7 +7780,9 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8025
7780
|
* @returns {Promise<Tweet | undefined>} A Promise that resolves to the cached tweet, or undefined if the tweet is not found in the cache.
|
|
8026
7781
|
*/
|
|
8027
7782
|
async getCachedTweet(tweetId) {
|
|
8028
|
-
const cached = await this.runtime.
|
|
7783
|
+
const cached = await this.runtime.getCache(
|
|
7784
|
+
`twitter/tweets/${tweetId}`
|
|
7785
|
+
);
|
|
8029
7786
|
if (!cached) {
|
|
8030
7787
|
return void 0;
|
|
8031
7788
|
}
|
|
@@ -8049,7 +7806,6 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8049
7806
|
await this.cacheTweet(tweet);
|
|
8050
7807
|
return tweet;
|
|
8051
7808
|
}
|
|
8052
|
-
callback = null;
|
|
8053
7809
|
/**
|
|
8054
7810
|
* This method is called when the application is ready.
|
|
8055
7811
|
* It throws an error indicating that it is not implemented in the base class
|
|
@@ -8124,19 +7880,6 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8124
7880
|
};
|
|
8125
7881
|
return t;
|
|
8126
7882
|
}
|
|
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
7883
|
async init() {
|
|
8141
7884
|
const username = this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME");
|
|
8142
7885
|
const password = this.state?.TWITTER_PASSWORD || this.runtime.getSetting("TWITTER_PASSWORD");
|
|
@@ -8151,28 +7894,25 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8151
7894
|
`Missing required Twitter credentials: ${missing.join(", ")}`
|
|
8152
7895
|
);
|
|
8153
7896
|
}
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
throw new Error("Twitter username not configured");
|
|
8159
|
-
}
|
|
8160
|
-
const authToken = this.state?.TWITTER_COOKIES_AUTH_TOKEN || this.runtime.getSetting("TWITTER_COOKIES_AUTH_TOKEN");
|
|
8161
|
-
const ct0 = this.state?.TWITTER_COOKIES_CT0 || this.runtime.getSetting("TWITTER_COOKIES_CT0");
|
|
8162
|
-
const guestId = this.state?.TWITTER_COOKIES_GUEST_ID || this.runtime.getSetting("TWITTER_COOKIES_GUEST_ID");
|
|
8163
|
-
const createTwitterCookies = (authToken2, ct02, guestId2) => authToken2 && ct02 && guestId2 ? [
|
|
8164
|
-
{ key: "auth_token", value: authToken2, domain: ".twitter.com" },
|
|
8165
|
-
{ key: "ct0", value: ct02, domain: ".twitter.com" },
|
|
8166
|
-
{ key: "guest_id", value: guestId2, domain: ".twitter.com" }
|
|
8167
|
-
] : null;
|
|
8168
|
-
const cachedCookies = await this.getCachedCookies(username) || createTwitterCookies(authToken, ct0, guestId);
|
|
8169
|
-
if (cachedCookies) {
|
|
8170
|
-
logger5.info("Using cached cookies");
|
|
8171
|
-
await this.setCookiesFromArray(cachedCookies);
|
|
8172
|
-
}
|
|
8173
|
-
logger5.log("Waiting for Twitter login");
|
|
8174
|
-
while (retries > 0) {
|
|
7897
|
+
const maxRetries = 3;
|
|
7898
|
+
let retryCount = 0;
|
|
7899
|
+
let lastError = null;
|
|
7900
|
+
while (retryCount < maxRetries) {
|
|
8175
7901
|
try {
|
|
7902
|
+
const authToken = this.state?.TWITTER_COOKIES_AUTH_TOKEN || this.runtime.getSetting("TWITTER_COOKIES_AUTH_TOKEN");
|
|
7903
|
+
const ct0 = this.state?.TWITTER_COOKIES_CT0 || this.runtime.getSetting("TWITTER_COOKIES_CT0");
|
|
7904
|
+
const guestId = this.state?.TWITTER_COOKIES_GUEST_ID || this.runtime.getSetting("TWITTER_COOKIES_GUEST_ID");
|
|
7905
|
+
const createTwitterCookies = (authToken2, ct02, guestId2) => authToken2 && ct02 && guestId2 ? [
|
|
7906
|
+
{ key: "auth_token", value: authToken2, domain: ".twitter.com" },
|
|
7907
|
+
{ key: "ct0", value: ct02, domain: ".twitter.com" },
|
|
7908
|
+
{ key: "guest_id", value: guestId2, domain: ".twitter.com" }
|
|
7909
|
+
] : null;
|
|
7910
|
+
const cachedCookies = await this.getCachedCookies(username) || createTwitterCookies(authToken, ct0, guestId);
|
|
7911
|
+
if (cachedCookies) {
|
|
7912
|
+
logger5.info("Using cached cookies");
|
|
7913
|
+
await this.setCookiesFromArray(cachedCookies);
|
|
7914
|
+
}
|
|
7915
|
+
logger5.log("Waiting for Twitter login");
|
|
8176
7916
|
if (await this.twitterClient.isLoggedIn()) {
|
|
8177
7917
|
logger5.info("Successfully logged in.");
|
|
8178
7918
|
break;
|
|
@@ -8193,17 +7933,22 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8193
7933
|
break;
|
|
8194
7934
|
}
|
|
8195
7935
|
} catch (error) {
|
|
8196
|
-
|
|
7936
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
7937
|
+
logger5.error(
|
|
7938
|
+
`Login attempt ${retryCount + 1} failed: ${lastError.message}`
|
|
7939
|
+
);
|
|
7940
|
+
retryCount++;
|
|
7941
|
+
if (retryCount < maxRetries) {
|
|
7942
|
+
const delay = 2 ** retryCount * 1e3;
|
|
7943
|
+
logger5.info(`Retrying in ${delay / 1e3} seconds...`);
|
|
7944
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7945
|
+
}
|
|
8197
7946
|
}
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
7947
|
+
}
|
|
7948
|
+
if (retryCount === maxRetries) {
|
|
7949
|
+
throw new Error(
|
|
7950
|
+
`Twitter login failed after ${maxRetries} attempts. Last error: ${lastError?.message}`
|
|
8201
7951
|
);
|
|
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
7952
|
}
|
|
8208
7953
|
this.profile = await this.fetchProfile(username);
|
|
8209
7954
|
if (this.profile) {
|
|
@@ -8417,7 +8162,7 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8417
8162
|
}
|
|
8418
8163
|
}
|
|
8419
8164
|
async loadLatestCheckedTweetId() {
|
|
8420
|
-
const latestCheckedTweetId = await this.runtime.
|
|
8165
|
+
const latestCheckedTweetId = await this.runtime.getCache(
|
|
8421
8166
|
`twitter/${this.profile.username}/latest_checked_tweet_id`
|
|
8422
8167
|
);
|
|
8423
8168
|
if (latestCheckedTweetId) {
|
|
@@ -8426,34 +8171,44 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8426
8171
|
}
|
|
8427
8172
|
async cacheLatestCheckedTweetId() {
|
|
8428
8173
|
if (this.lastCheckedTweetId) {
|
|
8429
|
-
await this.runtime.
|
|
8174
|
+
await this.runtime.setCache(
|
|
8430
8175
|
`twitter/${this.profile.username}/latest_checked_tweet_id`,
|
|
8431
8176
|
this.lastCheckedTweetId.toString()
|
|
8432
8177
|
);
|
|
8433
8178
|
}
|
|
8434
8179
|
}
|
|
8435
8180
|
async getCachedTimeline() {
|
|
8436
|
-
const cached = await this.runtime.
|
|
8181
|
+
const cached = await this.runtime.getCache(
|
|
8182
|
+
`twitter/${this.profile.username}/timeline`
|
|
8183
|
+
);
|
|
8437
8184
|
if (!cached) {
|
|
8438
8185
|
return void 0;
|
|
8439
8186
|
}
|
|
8440
8187
|
return cached;
|
|
8441
8188
|
}
|
|
8442
8189
|
async cacheTimeline(timeline) {
|
|
8443
|
-
await this.runtime.
|
|
8190
|
+
await this.runtime.setCache(
|
|
8191
|
+
`twitter/${this.profile.username}/timeline`,
|
|
8192
|
+
timeline
|
|
8193
|
+
);
|
|
8444
8194
|
}
|
|
8445
8195
|
async cacheMentions(mentions) {
|
|
8446
|
-
await this.runtime.
|
|
8196
|
+
await this.runtime.setCache(
|
|
8197
|
+
`twitter/${this.profile.username}/mentions`,
|
|
8198
|
+
mentions
|
|
8199
|
+
);
|
|
8447
8200
|
}
|
|
8448
8201
|
async getCachedCookies(username) {
|
|
8449
|
-
const cached = await this.runtime.
|
|
8202
|
+
const cached = await this.runtime.getCache(
|
|
8203
|
+
`twitter/${username}/cookies`
|
|
8204
|
+
);
|
|
8450
8205
|
if (!cached) {
|
|
8451
8206
|
return void 0;
|
|
8452
8207
|
}
|
|
8453
8208
|
return cached;
|
|
8454
8209
|
}
|
|
8455
8210
|
async cacheCookies(username, cookies) {
|
|
8456
|
-
await this.runtime.
|
|
8211
|
+
await this.runtime.setCache(`twitter/${username}/cookies`, cookies);
|
|
8457
8212
|
}
|
|
8458
8213
|
async fetchProfile(username) {
|
|
8459
8214
|
try {
|
|
@@ -8473,7 +8228,110 @@ var ClientBase = class _ClientBase extends EventEmitter6 {
|
|
|
8473
8228
|
throw error;
|
|
8474
8229
|
}
|
|
8475
8230
|
}
|
|
8231
|
+
/**
|
|
8232
|
+
* Fetches recent interactions (likes, retweets, quotes) for the authenticated user's tweets
|
|
8233
|
+
*/
|
|
8234
|
+
async fetchInteractions() {
|
|
8235
|
+
try {
|
|
8236
|
+
const interactions = await this.requestQueue.add(
|
|
8237
|
+
() => this.twitterClient.get("statuses/mentions_timeline", {
|
|
8238
|
+
count: 100,
|
|
8239
|
+
include_entities: true
|
|
8240
|
+
})
|
|
8241
|
+
);
|
|
8242
|
+
return interactions.map((interaction) => ({
|
|
8243
|
+
id: interaction.id_str,
|
|
8244
|
+
type: this.getInteractionType(interaction),
|
|
8245
|
+
userId: interaction.user.id_str,
|
|
8246
|
+
username: interaction.user.screen_name,
|
|
8247
|
+
name: interaction.user.name,
|
|
8248
|
+
targetTweetId: interaction.in_reply_to_status_id_str || interaction.quoted_status_id_str,
|
|
8249
|
+
targetTweet: interaction.quoted_status || interaction,
|
|
8250
|
+
quoteTweet: interaction.is_quote_status ? interaction : void 0,
|
|
8251
|
+
retweetId: interaction.retweeted_status?.id_str
|
|
8252
|
+
}));
|
|
8253
|
+
} catch (error) {
|
|
8254
|
+
logger5.error("Error fetching Twitter interactions:", error);
|
|
8255
|
+
return [];
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
/**
|
|
8259
|
+
* Determines the type of interaction from a Twitter API response
|
|
8260
|
+
*/
|
|
8261
|
+
getInteractionType(interaction) {
|
|
8262
|
+
if (interaction.retweeted_status) {
|
|
8263
|
+
return "retweet";
|
|
8264
|
+
}
|
|
8265
|
+
if (interaction.is_quote_status) {
|
|
8266
|
+
return "quote";
|
|
8267
|
+
}
|
|
8268
|
+
return "like";
|
|
8269
|
+
}
|
|
8270
|
+
/**
|
|
8271
|
+
* Fetches recent follower changes (new followers and unfollowers)
|
|
8272
|
+
*/
|
|
8273
|
+
async fetchFollowerChanges() {
|
|
8274
|
+
try {
|
|
8275
|
+
const currentFollowers = await this.requestQueue.add(
|
|
8276
|
+
() => this.twitterClient.get("followers/list", {
|
|
8277
|
+
count: 200,
|
|
8278
|
+
include_user_entities: false
|
|
8279
|
+
})
|
|
8280
|
+
);
|
|
8281
|
+
const cachedFollowers = await this.getCachedFollowers();
|
|
8282
|
+
const changes = [];
|
|
8283
|
+
for (const follower of currentFollowers.users) {
|
|
8284
|
+
if (!cachedFollowers.some(
|
|
8285
|
+
(f) => f.id_str === follower.id_str
|
|
8286
|
+
)) {
|
|
8287
|
+
changes.push({
|
|
8288
|
+
type: "followed",
|
|
8289
|
+
userId: follower.id_str,
|
|
8290
|
+
username: follower.screen_name,
|
|
8291
|
+
name: follower.name,
|
|
8292
|
+
user: follower
|
|
8293
|
+
});
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8296
|
+
for (const cached of cachedFollowers) {
|
|
8297
|
+
if (!currentFollowers.users.some((f) => f.id_str === cached.id_str)) {
|
|
8298
|
+
changes.push({
|
|
8299
|
+
type: "unfollowed",
|
|
8300
|
+
userId: cached.id_str,
|
|
8301
|
+
username: cached.screen_name,
|
|
8302
|
+
name: cached.name,
|
|
8303
|
+
user: cached
|
|
8304
|
+
});
|
|
8305
|
+
}
|
|
8306
|
+
}
|
|
8307
|
+
await this.cacheFollowers(currentFollowers.users);
|
|
8308
|
+
return changes;
|
|
8309
|
+
} catch (error) {
|
|
8310
|
+
logger5.error("Error fetching Twitter follower changes:", error);
|
|
8311
|
+
return [];
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
8314
|
+
/**
|
|
8315
|
+
* Gets cached followers from the database
|
|
8316
|
+
*/
|
|
8317
|
+
async getCachedFollowers() {
|
|
8318
|
+
const cached = await this.runtime.getCache(
|
|
8319
|
+
`twitter/${this.profile.username}/followers`
|
|
8320
|
+
);
|
|
8321
|
+
return cached || [];
|
|
8322
|
+
}
|
|
8323
|
+
/**
|
|
8324
|
+
* Caches current followers in the database
|
|
8325
|
+
*/
|
|
8326
|
+
async cacheFollowers(followers) {
|
|
8327
|
+
await this.runtime.setCache(
|
|
8328
|
+
`twitter/${this.profile.username}/followers`,
|
|
8329
|
+
followers
|
|
8330
|
+
);
|
|
8331
|
+
}
|
|
8476
8332
|
};
|
|
8333
|
+
_ClientBase._twitterClients = {};
|
|
8334
|
+
var ClientBase = _ClientBase;
|
|
8477
8335
|
|
|
8478
8336
|
// src/constants.ts
|
|
8479
8337
|
var TWITTER_SERVICE_NAME = "twitter";
|
|
@@ -8481,58 +8339,38 @@ var TWITTER_SERVICE_NAME = "twitter";
|
|
|
8481
8339
|
// src/interactions.ts
|
|
8482
8340
|
import {
|
|
8483
8341
|
ChannelType as ChannelType4,
|
|
8342
|
+
EventTypes as EventTypes2,
|
|
8484
8343
|
ModelTypes as ModelTypes4,
|
|
8485
8344
|
composePrompt as composePrompt2,
|
|
8486
8345
|
createUniqueUuid as createUniqueUuid4,
|
|
8487
|
-
logger as logger6
|
|
8488
|
-
parseJSONObjectFromText
|
|
8346
|
+
logger as logger6
|
|
8489
8347
|
} 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
|
-
|
|
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
8348
|
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
Recent Posts:
|
|
8521
|
-
{{recentPosts}}
|
|
8522
|
-
|
|
8523
|
-
Current Post:
|
|
8524
|
-
{{currentPost}}
|
|
8525
|
-
|
|
8526
|
-
Thread of Tweets You Are Replying To:
|
|
8527
|
-
{{formattedConversation}}
|
|
8349
|
+
// src/types.ts
|
|
8350
|
+
var ServiceTypes2 = {
|
|
8351
|
+
TWITTER: "twitter"
|
|
8352
|
+
};
|
|
8528
8353
|
|
|
8529
|
-
|
|
8530
|
-
|
|
8354
|
+
// src/interactions.ts
|
|
8355
|
+
var convertToCoreTweet = (tweet) => ({
|
|
8356
|
+
id: tweet.id,
|
|
8357
|
+
text: tweet.text,
|
|
8358
|
+
conversationId: tweet.conversationId,
|
|
8359
|
+
timestamp: tweet.timestamp,
|
|
8360
|
+
userId: tweet.userId,
|
|
8361
|
+
username: tweet.username,
|
|
8362
|
+
name: tweet.name,
|
|
8363
|
+
inReplyToStatusId: tweet.inReplyToStatusId,
|
|
8364
|
+
permanentUrl: tweet.permanentUrl,
|
|
8365
|
+
photos: tweet.photos,
|
|
8366
|
+
hashtags: tweet.hashtags,
|
|
8367
|
+
mentions: tweet.mentions.map((mention) => mention.username),
|
|
8368
|
+
urls: tweet.urls,
|
|
8369
|
+
videos: tweet.videos,
|
|
8370
|
+
thread: tweet.thread
|
|
8371
|
+
});
|
|
8372
|
+
var convertToCoreTweets = (tweets) => tweets.map(convertToCoreTweet);
|
|
8531
8373
|
var TwitterInteractionClient = class {
|
|
8532
|
-
client;
|
|
8533
|
-
runtime;
|
|
8534
|
-
isDryRun;
|
|
8535
|
-
state;
|
|
8536
8374
|
/**
|
|
8537
8375
|
* Constructor for setting up a new instance with the provided client, runtime, and state.
|
|
8538
8376
|
* @param {ClientBase} client - The client being used for communication.
|
|
@@ -8611,6 +8449,68 @@ var TwitterInteractionClient = class {
|
|
|
8611
8449
|
entityId,
|
|
8612
8450
|
roomId
|
|
8613
8451
|
};
|
|
8452
|
+
if (tweet.text.includes(`@${twitterUsername}`)) {
|
|
8453
|
+
const messagePayload = {
|
|
8454
|
+
runtime: this.runtime,
|
|
8455
|
+
message: {
|
|
8456
|
+
...message,
|
|
8457
|
+
content: {
|
|
8458
|
+
...message.content,
|
|
8459
|
+
source: "twitter"
|
|
8460
|
+
},
|
|
8461
|
+
roomId: message.roomId
|
|
8462
|
+
},
|
|
8463
|
+
source: "twitter",
|
|
8464
|
+
callback: async (response) => {
|
|
8465
|
+
logger6.info("Received message response:", response);
|
|
8466
|
+
return [];
|
|
8467
|
+
}
|
|
8468
|
+
};
|
|
8469
|
+
this.runtime.emitEvent(EventTypes2.MESSAGE_RECEIVED, messagePayload);
|
|
8470
|
+
const mentionPayload = {
|
|
8471
|
+
runtime: this.runtime,
|
|
8472
|
+
message: {
|
|
8473
|
+
...message,
|
|
8474
|
+
content: {
|
|
8475
|
+
...message.content,
|
|
8476
|
+
source: "twitter"
|
|
8477
|
+
},
|
|
8478
|
+
roomId: message.roomId
|
|
8479
|
+
},
|
|
8480
|
+
tweet: convertToCoreTweet(tweet),
|
|
8481
|
+
user: {
|
|
8482
|
+
id: tweet.userId,
|
|
8483
|
+
username: tweet.username,
|
|
8484
|
+
name: tweet.name
|
|
8485
|
+
},
|
|
8486
|
+
source: "twitter",
|
|
8487
|
+
callback: async (response) => {
|
|
8488
|
+
logger6.info("Received mention response:", response);
|
|
8489
|
+
return [];
|
|
8490
|
+
}
|
|
8491
|
+
};
|
|
8492
|
+
this.runtime.emitEvent("TWITTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
|
|
8493
|
+
}
|
|
8494
|
+
if (thread.length > 1) {
|
|
8495
|
+
const threadPayload = {
|
|
8496
|
+
runtime: this.runtime,
|
|
8497
|
+
tweets: convertToCoreTweets(thread),
|
|
8498
|
+
user: {
|
|
8499
|
+
id: tweet.userId,
|
|
8500
|
+
username: tweet.username,
|
|
8501
|
+
name: tweet.name
|
|
8502
|
+
},
|
|
8503
|
+
source: "twitter"
|
|
8504
|
+
};
|
|
8505
|
+
if (thread[thread.length - 1].id === tweet.id) {
|
|
8506
|
+
this.runtime.emitEvent("TWITTER_THREAD_UPDATED" /* THREAD_UPDATED */, {
|
|
8507
|
+
...threadPayload,
|
|
8508
|
+
newTweet: convertToCoreTweet(tweet)
|
|
8509
|
+
});
|
|
8510
|
+
} else if (thread[0].id === tweet.id) {
|
|
8511
|
+
this.runtime.emitEvent("TWITTER_THREAD_CREATED" /* THREAD_CREATED */, threadPayload);
|
|
8512
|
+
}
|
|
8513
|
+
}
|
|
8614
8514
|
await this.handleTweet({
|
|
8615
8515
|
tweet,
|
|
8616
8516
|
message,
|
|
@@ -8619,6 +8519,156 @@ var TwitterInteractionClient = class {
|
|
|
8619
8519
|
this.client.lastCheckedTweetId = BigInt(tweet.id);
|
|
8620
8520
|
}
|
|
8621
8521
|
}
|
|
8522
|
+
const interactions = await this.client.fetchInteractions();
|
|
8523
|
+
const handleInteraction = async (interaction) => {
|
|
8524
|
+
if (interaction?.targetTweet?.conversationId) {
|
|
8525
|
+
const memory = this.createMemoryObject(
|
|
8526
|
+
interaction.type,
|
|
8527
|
+
`${interaction.id}-${interaction.type}`,
|
|
8528
|
+
interaction.userId,
|
|
8529
|
+
interaction.targetTweet.conversationId
|
|
8530
|
+
);
|
|
8531
|
+
await this.runtime.getMemoryManager("interactions").createMemory(memory);
|
|
8532
|
+
const reactionMessage = {
|
|
8533
|
+
id: createUniqueUuid4(this.runtime, interaction.targetTweetId),
|
|
8534
|
+
content: {
|
|
8535
|
+
text: interaction.targetTweet.text,
|
|
8536
|
+
source: "twitter"
|
|
8537
|
+
},
|
|
8538
|
+
entityId: createUniqueUuid4(this.runtime, interaction.targetTweet.userId),
|
|
8539
|
+
roomId: createUniqueUuid4(this.runtime, interaction.targetTweet.conversationId),
|
|
8540
|
+
agentId: this.runtime.agentId
|
|
8541
|
+
};
|
|
8542
|
+
const basePayload = {
|
|
8543
|
+
runtime: this.runtime,
|
|
8544
|
+
user: {
|
|
8545
|
+
id: interaction.userId,
|
|
8546
|
+
username: interaction.username,
|
|
8547
|
+
name: interaction.name
|
|
8548
|
+
},
|
|
8549
|
+
source: "twitter"
|
|
8550
|
+
};
|
|
8551
|
+
switch (interaction.type) {
|
|
8552
|
+
case "like": {
|
|
8553
|
+
const likePayload = {
|
|
8554
|
+
...basePayload,
|
|
8555
|
+
tweet: interaction.targetTweet
|
|
8556
|
+
};
|
|
8557
|
+
this.runtime.emitEvent("TWITTER_LIKE_RECEIVED" /* LIKE_RECEIVED */, likePayload);
|
|
8558
|
+
this.runtime.emitEvent(EventTypes2.REACTION_RECEIVED, {
|
|
8559
|
+
...basePayload,
|
|
8560
|
+
reaction: {
|
|
8561
|
+
type: "like",
|
|
8562
|
+
entityId: createUniqueUuid4(this.runtime, interaction.userId)
|
|
8563
|
+
},
|
|
8564
|
+
message: reactionMessage,
|
|
8565
|
+
callback: async () => {
|
|
8566
|
+
return [];
|
|
8567
|
+
}
|
|
8568
|
+
});
|
|
8569
|
+
break;
|
|
8570
|
+
}
|
|
8571
|
+
case "retweet": {
|
|
8572
|
+
const retweetPayload = {
|
|
8573
|
+
...basePayload,
|
|
8574
|
+
tweet: interaction.targetTweet,
|
|
8575
|
+
retweetId: interaction.retweetId
|
|
8576
|
+
};
|
|
8577
|
+
this.runtime.emitEvent("TWITTER_RETWEET_RECEIVED" /* RETWEET_RECEIVED */, retweetPayload);
|
|
8578
|
+
this.runtime.emitEvent(EventTypes2.REACTION_RECEIVED, {
|
|
8579
|
+
...basePayload,
|
|
8580
|
+
reaction: {
|
|
8581
|
+
type: "retweet",
|
|
8582
|
+
entityId: createUniqueUuid4(this.runtime, interaction.userId)
|
|
8583
|
+
},
|
|
8584
|
+
message: reactionMessage,
|
|
8585
|
+
callback: async () => {
|
|
8586
|
+
return [];
|
|
8587
|
+
}
|
|
8588
|
+
});
|
|
8589
|
+
break;
|
|
8590
|
+
}
|
|
8591
|
+
case "quote": {
|
|
8592
|
+
const quotePayload = {
|
|
8593
|
+
...basePayload,
|
|
8594
|
+
message: reactionMessage,
|
|
8595
|
+
quotedTweet: interaction.targetTweet,
|
|
8596
|
+
quoteTweet: interaction.quoteTweet || interaction.targetTweet,
|
|
8597
|
+
callback: async () => [],
|
|
8598
|
+
reaction: {
|
|
8599
|
+
type: "quote",
|
|
8600
|
+
entityId: createUniqueUuid4(this.runtime, interaction.userId)
|
|
8601
|
+
}
|
|
8602
|
+
};
|
|
8603
|
+
this.runtime.emitEvent("TWITTER_QUOTE_RECEIVED" /* QUOTE_RECEIVED */, quotePayload);
|
|
8604
|
+
this.runtime.emitEvent(EventTypes2.REACTION_RECEIVED, {
|
|
8605
|
+
...basePayload,
|
|
8606
|
+
reaction: {
|
|
8607
|
+
type: "quote",
|
|
8608
|
+
entityId: createUniqueUuid4(this.runtime, interaction.userId)
|
|
8609
|
+
},
|
|
8610
|
+
message: reactionMessage,
|
|
8611
|
+
callback: async () => {
|
|
8612
|
+
return [];
|
|
8613
|
+
}
|
|
8614
|
+
});
|
|
8615
|
+
break;
|
|
8616
|
+
}
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
};
|
|
8620
|
+
const processInteractions = async (interactions2) => {
|
|
8621
|
+
for (const interaction of interactions2) {
|
|
8622
|
+
if (interaction?.targetTweet?.conversationId) {
|
|
8623
|
+
await handleInteraction(interaction);
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8626
|
+
};
|
|
8627
|
+
const processFollowerChange = async (change, profileId) => {
|
|
8628
|
+
if (change?.type && change?.userId && profileId) {
|
|
8629
|
+
const followerMemory = this.createMemoryObject(
|
|
8630
|
+
change.type,
|
|
8631
|
+
`${change.type}-${change.userId}`,
|
|
8632
|
+
change.userId,
|
|
8633
|
+
profileId
|
|
8634
|
+
);
|
|
8635
|
+
await this.runtime.getMemoryManager("follower-changes").createMemory(followerMemory);
|
|
8636
|
+
}
|
|
8637
|
+
};
|
|
8638
|
+
const followerChanges = await this.client.fetchFollowerChanges();
|
|
8639
|
+
for (const change of followerChanges) {
|
|
8640
|
+
const changeId = createUniqueUuid4(this.runtime, `${change.type}-${change.userId}`);
|
|
8641
|
+
const existingChange = await this.runtime.getMemoryManager("follower-changes").getMemoryById(changeId);
|
|
8642
|
+
if (existingChange) continue;
|
|
8643
|
+
const entityId = createUniqueUuid4(this.runtime, change.userId);
|
|
8644
|
+
const basePayload = {
|
|
8645
|
+
runtime: this.runtime,
|
|
8646
|
+
user: {
|
|
8647
|
+
id: change.userId,
|
|
8648
|
+
username: change.username,
|
|
8649
|
+
name: change.name
|
|
8650
|
+
},
|
|
8651
|
+
source: "twitter"
|
|
8652
|
+
};
|
|
8653
|
+
if (change.type === "followed") {
|
|
8654
|
+
const userFollowedPayload = {
|
|
8655
|
+
...basePayload,
|
|
8656
|
+
follower: change.user,
|
|
8657
|
+
entityId: createUniqueUuid4(this.runtime, change.userId),
|
|
8658
|
+
roomId: createUniqueUuid4(this.runtime, this.client.profile.id)
|
|
8659
|
+
};
|
|
8660
|
+
this.runtime.emitEvent("TWITTER_USER_FOLLOWED" /* USER_FOLLOWED */, userFollowedPayload);
|
|
8661
|
+
} else if (change.type === "unfollowed") {
|
|
8662
|
+
const userUnfollowedPayload = {
|
|
8663
|
+
...basePayload,
|
|
8664
|
+
unfollower: change.user,
|
|
8665
|
+
entityId: createUniqueUuid4(this.runtime, change.userId),
|
|
8666
|
+
roomId: createUniqueUuid4(this.runtime, this.client.profile.id)
|
|
8667
|
+
};
|
|
8668
|
+
this.runtime.emitEvent("TWITTER_USER_UNFOLLOWED" /* USER_UNFOLLOWED */, userUnfollowedPayload);
|
|
8669
|
+
}
|
|
8670
|
+
await processFollowerChange(change, this.client.profile.id);
|
|
8671
|
+
}
|
|
8622
8672
|
await this.client.cacheLatestCheckedTweetId();
|
|
8623
8673
|
logger6.log("Finished checking Twitter interactions");
|
|
8624
8674
|
} catch (error) {
|
|
@@ -8676,12 +8726,11 @@ var TwitterInteractionClient = class {
|
|
|
8676
8726
|
} catch (error) {
|
|
8677
8727
|
logger6.error("Error Occured during describing image: ", error);
|
|
8678
8728
|
}
|
|
8679
|
-
|
|
8729
|
+
const state = await this.runtime.composeState(message);
|
|
8680
8730
|
state.values = {
|
|
8681
8731
|
...state.values,
|
|
8682
8732
|
twitterUserName: this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME"),
|
|
8683
8733
|
currentPost,
|
|
8684
|
-
// TODO: move to recentMessages provider
|
|
8685
8734
|
formattedConversation
|
|
8686
8735
|
};
|
|
8687
8736
|
const tweetId = createUniqueUuid4(this.runtime, tweet.id);
|
|
@@ -8698,117 +8747,96 @@ var TwitterInteractionClient = class {
|
|
|
8698
8747
|
source: "twitter",
|
|
8699
8748
|
type: ChannelType4.GROUP
|
|
8700
8749
|
});
|
|
8701
|
-
|
|
8750
|
+
await this.runtime.ensureRoomExists({
|
|
8751
|
+
id: roomId,
|
|
8752
|
+
name: `Conversation with ${tweet.name}`,
|
|
8753
|
+
source: "twitter",
|
|
8754
|
+
type: ChannelType4.GROUP,
|
|
8755
|
+
channelId: tweet.conversationId,
|
|
8756
|
+
worldId: createUniqueUuid4(this.runtime, tweet.userId)
|
|
8757
|
+
});
|
|
8758
|
+
const memory = {
|
|
8702
8759
|
id: tweetId,
|
|
8703
8760
|
agentId: this.runtime.agentId,
|
|
8704
8761
|
content: {
|
|
8705
8762
|
text: tweet.text,
|
|
8706
8763
|
url: tweet.permanentUrl,
|
|
8707
8764
|
imageUrls: tweet.photos?.map((photo) => photo.url) || [],
|
|
8708
|
-
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0
|
|
8765
|
+
inReplyTo: tweet.inReplyToStatusId ? createUniqueUuid4(this.runtime, tweet.inReplyToStatusId) : void 0,
|
|
8766
|
+
source: "twitter",
|
|
8767
|
+
channelType: ChannelType4.GROUP
|
|
8709
8768
|
},
|
|
8710
8769
|
entityId,
|
|
8711
8770
|
roomId,
|
|
8712
8771
|
createdAt: tweet.timestamp * 1e3
|
|
8713
8772
|
};
|
|
8714
|
-
this.client.saveRequestMessage(
|
|
8773
|
+
this.client.saveRequestMessage(memory, state);
|
|
8715
8774
|
}
|
|
8716
8775
|
const shouldRespondPrompt = composePrompt2({
|
|
8717
8776
|
state,
|
|
8718
|
-
template: this.runtime.character.templates?.
|
|
8777
|
+
template: this.runtime.character.templates?.shouldRespondTemplate || ""
|
|
8719
8778
|
});
|
|
8720
|
-
const
|
|
8779
|
+
const response = await this.runtime.useModel(ModelTypes4.TEXT_SMALL, {
|
|
8721
8780
|
prompt: shouldRespondPrompt
|
|
8722
8781
|
});
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8782
|
+
const responseActions = (response.match(/(?:RESPOND|IGNORE|STOP)/g) || [
|
|
8783
|
+
"IGNORE"
|
|
8784
|
+
])[0];
|
|
8785
|
+
if (responseActions !== "RESPOND") {
|
|
8786
|
+
logger6.log(
|
|
8787
|
+
`Not responding to tweet based on shouldRespond decision: ${responseActions}`
|
|
8788
|
+
);
|
|
8789
|
+
return { text: "", actions: [responseActions] };
|
|
8726
8790
|
}
|
|
8727
|
-
const
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
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") : ""
|
|
8791
|
+
const callback = async (response2, tweetId2) => {
|
|
8792
|
+
try {
|
|
8793
|
+
const tweetToReplyTo = tweetId2 || tweet.id;
|
|
8794
|
+
if (this.isDryRun) {
|
|
8795
|
+
logger6.info(
|
|
8796
|
+
`[DRY RUN] Would have replied to ${tweet.username} with: ${response2.text}`
|
|
8797
|
+
);
|
|
8798
|
+
return [];
|
|
8741
8799
|
}
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
const removeQuotes = (str) => str.replace(/^['"](.*)['"]$/, "$1");
|
|
8750
|
-
const replyToId = createUniqueUuid4(this.runtime, tweet.id);
|
|
8751
|
-
response.inReplyTo = replyToId;
|
|
8752
|
-
response.text = removeQuotes(response.text);
|
|
8753
|
-
if (response.text) {
|
|
8754
|
-
if (this.isDryRun) {
|
|
8755
|
-
logger6.info(
|
|
8756
|
-
`Dry run: Selected Post: ${tweet.id} - ${tweet.username}: ${tweet.text}
|
|
8757
|
-
Agent's Output:
|
|
8758
|
-
${response.text}`
|
|
8800
|
+
logger6.info(`Replying to tweet ${tweetToReplyTo}`);
|
|
8801
|
+
const replyTweetResult = await this.client.requestQueue.add(
|
|
8802
|
+
() => this.client.twitterClient.post("statuses/update", {
|
|
8803
|
+
status: response2.text.substring(0, 280),
|
|
8804
|
+
in_reply_to_status_id: tweetToReplyTo,
|
|
8805
|
+
auto_populate_reply_metadata: true
|
|
8806
|
+
})
|
|
8759
8807
|
);
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
const callback = async (response2, tweetId2) => {
|
|
8763
|
-
const memories = await sendTweet(
|
|
8764
|
-
this.client,
|
|
8765
|
-
response2,
|
|
8766
|
-
message.roomId,
|
|
8767
|
-
this.state?.TWITTER_USERNAME || this.runtime.getSetting("TWITTER_USERNAME"),
|
|
8768
|
-
tweetId2 || tweet.id
|
|
8769
|
-
);
|
|
8770
|
-
return memories;
|
|
8771
|
-
};
|
|
8772
|
-
const responseMessages = [
|
|
8773
|
-
{
|
|
8774
|
-
id: createUniqueUuid4(this.runtime, tweet.id),
|
|
8775
|
-
entityId: this.runtime.agentId,
|
|
8776
|
-
agentId: this.runtime.agentId,
|
|
8777
|
-
content: response,
|
|
8778
|
-
roomId: message.roomId,
|
|
8779
|
-
createdAt: Date.now()
|
|
8780
|
-
}
|
|
8781
|
-
];
|
|
8782
|
-
state = await this.runtime.composeState(message, ["RECENT_MESSAGES"]);
|
|
8783
|
-
for (const responseMessage of responseMessages) {
|
|
8784
|
-
await this.runtime.getMemoryManager("messages").createMemory(responseMessage);
|
|
8785
|
-
}
|
|
8786
|
-
const responseTweetId = responseMessages[responseMessages.length - 1]?.content?.tweetId;
|
|
8787
|
-
await this.runtime.processActions(
|
|
8788
|
-
message,
|
|
8789
|
-
responseMessages,
|
|
8790
|
-
state,
|
|
8791
|
-
(response2) => {
|
|
8792
|
-
return callback(response2, responseTweetId);
|
|
8793
|
-
}
|
|
8794
|
-
);
|
|
8795
|
-
const responseInfo = `Context:
|
|
8796
|
-
|
|
8797
|
-
${prompt}
|
|
8798
|
-
|
|
8799
|
-
Selected Post: ${tweet.id} - ${tweet.username}: ${tweet.text}
|
|
8800
|
-
Agent's Output:
|
|
8801
|
-
${response.text}`;
|
|
8802
|
-
await this.runtime.getDatabaseAdapter().setCache(
|
|
8803
|
-
`twitter/tweet_generation_${tweet.id}.txt`,
|
|
8804
|
-
responseInfo
|
|
8805
|
-
);
|
|
8806
|
-
await wait();
|
|
8807
|
-
} catch (error) {
|
|
8808
|
-
logger6.error(`Error sending response tweet: ${error}`);
|
|
8808
|
+
if (!replyTweetResult) {
|
|
8809
|
+
throw new Error("Failed to create tweet response");
|
|
8809
8810
|
}
|
|
8811
|
+
const responseId = createUniqueUuid4(
|
|
8812
|
+
this.runtime,
|
|
8813
|
+
replyTweetResult.id_str
|
|
8814
|
+
);
|
|
8815
|
+
const responseMemory = {
|
|
8816
|
+
id: responseId,
|
|
8817
|
+
entityId: this.runtime.agentId,
|
|
8818
|
+
agentId: this.runtime.agentId,
|
|
8819
|
+
roomId: message.roomId,
|
|
8820
|
+
content: {
|
|
8821
|
+
...response2,
|
|
8822
|
+
inReplyTo: message.id
|
|
8823
|
+
},
|
|
8824
|
+
createdAt: Date.now()
|
|
8825
|
+
};
|
|
8826
|
+
await this.runtime.getMemoryManager("messages").createMemory(responseMemory);
|
|
8827
|
+
return [responseMemory];
|
|
8828
|
+
} catch (error) {
|
|
8829
|
+
logger6.error("Error replying to tweet:", error);
|
|
8830
|
+
return [];
|
|
8810
8831
|
}
|
|
8811
|
-
}
|
|
8832
|
+
};
|
|
8833
|
+
this.runtime.emitEvent(EventTypes2.MESSAGE_RECEIVED, {
|
|
8834
|
+
runtime: this.runtime,
|
|
8835
|
+
message,
|
|
8836
|
+
callback,
|
|
8837
|
+
source: "twitter"
|
|
8838
|
+
});
|
|
8839
|
+
return { text: "", actions: ["RESPOND"] };
|
|
8812
8840
|
}
|
|
8813
8841
|
/**
|
|
8814
8842
|
* Build a conversation thread based on a given tweet.
|
|
@@ -8898,18 +8926,29 @@ ${response.text}`;
|
|
|
8898
8926
|
await processThread.bind(this)(tweet, 0);
|
|
8899
8927
|
return thread;
|
|
8900
8928
|
}
|
|
8929
|
+
createMemoryObject(type, id, userId, roomId) {
|
|
8930
|
+
return {
|
|
8931
|
+
id: createUniqueUuid4(this.runtime, id),
|
|
8932
|
+
agentId: this.runtime.agentId,
|
|
8933
|
+
entityId: createUniqueUuid4(this.runtime, userId),
|
|
8934
|
+
roomId: createUniqueUuid4(this.runtime, roomId),
|
|
8935
|
+
content: {
|
|
8936
|
+
type,
|
|
8937
|
+
source: "twitter"
|
|
8938
|
+
},
|
|
8939
|
+
createdAt: Date.now()
|
|
8940
|
+
};
|
|
8941
|
+
}
|
|
8901
8942
|
};
|
|
8902
8943
|
|
|
8903
8944
|
// src/post.ts
|
|
8904
8945
|
import {
|
|
8905
8946
|
ChannelType as ChannelType5,
|
|
8906
8947
|
ModelTypes as ModelTypes5,
|
|
8907
|
-
cleanJsonResponse,
|
|
8908
8948
|
composePrompt as composePrompt3,
|
|
8909
8949
|
createUniqueUuid as createUniqueUuid5,
|
|
8910
|
-
extractAttributes,
|
|
8911
8950
|
logger as logger7,
|
|
8912
|
-
parseJSONObjectFromText
|
|
8951
|
+
parseJSONObjectFromText,
|
|
8913
8952
|
truncateToCompleteSentence
|
|
8914
8953
|
} from "@elizaos/core";
|
|
8915
8954
|
|
|
@@ -8922,11 +8961,6 @@ Your response should not contain any questions. Brief, concise statements only.
|
|
|
8922
8961
|
|
|
8923
8962
|
// src/post.ts
|
|
8924
8963
|
var TwitterPostClient = class {
|
|
8925
|
-
client;
|
|
8926
|
-
runtime;
|
|
8927
|
-
twitterUsername;
|
|
8928
|
-
isDryRun;
|
|
8929
|
-
state;
|
|
8930
8964
|
/**
|
|
8931
8965
|
* Constructor for initializing a new Twitter client with the provided client, runtime, and state
|
|
8932
8966
|
* @param {ClientBase} client - The client used for interacting with Twitter API
|
|
@@ -8941,7 +8975,7 @@ var TwitterPostClient = class {
|
|
|
8941
8975
|
this.isDryRun = this.state?.TWITTER_DRY_RUN || this.runtime.getSetting("TWITTER_DRY_RUN");
|
|
8942
8976
|
logger7.log("Twitter Client Configuration:");
|
|
8943
8977
|
logger7.log(`- Username: ${this.twitterUsername}`);
|
|
8944
|
-
logger7.log(`- Dry Run Mode: ${this.isDryRun ? "
|
|
8978
|
+
logger7.log(`- Dry Run Mode: ${this.isDryRun ? "Enabled" : "Disabled"}`);
|
|
8945
8979
|
logger7.log(
|
|
8946
8980
|
`- Disable Post: ${this.state?.TWITTER_ENABLE_POST_GENERATION || this.runtime.getSetting("TWITTER_ENABLE_POST_GENERATION") ? "disabled" : "enabled"}`
|
|
8947
8981
|
);
|
|
@@ -8958,44 +8992,21 @@ var TwitterPostClient = class {
|
|
|
8958
8992
|
}
|
|
8959
8993
|
}
|
|
8960
8994
|
/**
|
|
8961
|
-
*
|
|
8962
|
-
* If the client's profile is not available, it initializes the client first.
|
|
8963
|
-
* It generates a new tweet at random intervals specified by the TWITTER_POST_INTERVAL_MIN and TWITTER_POST_INTERVAL_MAX settings or state properties.
|
|
8964
|
-
* Optionally, it can immediately generate a tweet if TWITTER_POST_IMMEDIATELY is set to true in the state or settings.
|
|
8995
|
+
* Starts the Twitter post client, setting up a loop to periodically generate new tweets.
|
|
8965
8996
|
*/
|
|
8966
8997
|
async start() {
|
|
8967
|
-
|
|
8968
|
-
|
|
8998
|
+
logger7.log("Starting Twitter post client...");
|
|
8999
|
+
const tweetGeneration = this.runtime.getSetting("TWITTER_ENABLE_TWEET_GENERATION");
|
|
9000
|
+
if (tweetGeneration === false) {
|
|
9001
|
+
logger7.log("Tweet generation is disabled");
|
|
9002
|
+
return;
|
|
8969
9003
|
}
|
|
8970
9004
|
const generateNewTweetLoop = async () => {
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
timestamp: 0
|
|
8975
|
-
});
|
|
8976
|
-
}
|
|
8977
|
-
const lastPostTimestamp = lastPost.timestamp ?? 0;
|
|
8978
|
-
const minMinutes = (this.state?.TWITTER_POST_INTERVAL_MIN || this.runtime.getSetting("TWITTER_POST_INTERVAL_MIN")) ?? 90;
|
|
8979
|
-
const maxMinutes = (this.state?.TWITTER_POST_INTERVAL_MAX || this.runtime.getSetting("TWITTER_POST_INTERVAL_MAX")) ?? 180;
|
|
8980
|
-
const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes;
|
|
8981
|
-
const delay = randomMinutes * 60 * 1e3;
|
|
8982
|
-
if (Date.now() > lastPostTimestamp + delay) {
|
|
8983
|
-
await this.generateNewTweet();
|
|
8984
|
-
}
|
|
8985
|
-
setTimeout(() => {
|
|
8986
|
-
generateNewTweetLoop();
|
|
8987
|
-
}, delay);
|
|
8988
|
-
logger7.log(`Next tweet scheduled in ${randomMinutes} minutes`);
|
|
9005
|
+
const interval = (this.state?.TWITTER_POST_INTERVAL || this.runtime.getSetting("TWITTER_POST_INTERVAL") || 30) * 60 * 1e3;
|
|
9006
|
+
this.generateNewTweet();
|
|
9007
|
+
setTimeout(generateNewTweetLoop, interval);
|
|
8989
9008
|
};
|
|
8990
|
-
|
|
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");
|
|
8998
|
-
}
|
|
9009
|
+
setTimeout(generateNewTweetLoop, 60 * 1e3);
|
|
8999
9010
|
}
|
|
9000
9011
|
/**
|
|
9001
9012
|
* Creates a Tweet object based on the tweet result, client information, and Twitter username.
|
|
@@ -9035,7 +9046,7 @@ var TwitterPostClient = class {
|
|
|
9035
9046
|
* @param {string} rawTweetContent - The raw content of the tweet.
|
|
9036
9047
|
*/
|
|
9037
9048
|
async processAndCacheTweet(runtime, client, tweet, roomId, rawTweetContent) {
|
|
9038
|
-
await runtime.
|
|
9049
|
+
await runtime.setCache(`twitter/${client.profile.username}/lastPost`, {
|
|
9039
9050
|
id: tweet.id,
|
|
9040
9051
|
timestamp: Date.now()
|
|
9041
9052
|
});
|
|
@@ -9156,95 +9167,169 @@ var TwitterPostClient = class {
|
|
|
9156
9167
|
}
|
|
9157
9168
|
}
|
|
9158
9169
|
/**
|
|
9159
|
-
*
|
|
9160
|
-
|
|
9161
|
-
/**
|
|
9162
|
-
* Asynchronously generates a new tweet for the Twitter account associated with the agent.
|
|
9163
|
-
* This method retrieves random topics of interest from the character's topics list and prompts the user to compose a tweet based on those topics.
|
|
9164
|
-
* The tweet is then processed and posted on Twitter with optional media attachments.
|
|
9165
|
-
* @returns {void}
|
|
9170
|
+
* Handles the creation and posting of a tweet by emitting standardized events.
|
|
9171
|
+
* This approach aligns with our platform-independent architecture.
|
|
9166
9172
|
*/
|
|
9167
9173
|
async generateNewTweet() {
|
|
9168
|
-
logger7.log("Generating new tweet");
|
|
9169
9174
|
try {
|
|
9170
|
-
|
|
9171
|
-
const
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
};
|
|
9185
|
-
const prompt = composePrompt3({
|
|
9175
|
+
logger7.log("Generating new tweet...");
|
|
9176
|
+
const userId = this.client.profile?.id;
|
|
9177
|
+
if (!userId) {
|
|
9178
|
+
logger7.error("Cannot generate tweet: Twitter profile not available");
|
|
9179
|
+
return;
|
|
9180
|
+
}
|
|
9181
|
+
const worldId = createUniqueUuid5(this.runtime, userId);
|
|
9182
|
+
const timelineRoomId = createUniqueUuid5(this.runtime, `${userId}-home`);
|
|
9183
|
+
const state = await this.runtime.composeState(null, [
|
|
9184
|
+
"CHARACTER",
|
|
9185
|
+
"RECENT_MESSAGES",
|
|
9186
|
+
"TIME"
|
|
9187
|
+
]);
|
|
9188
|
+
const tweetPrompt = composePrompt3({
|
|
9186
9189
|
state,
|
|
9187
9190
|
template: this.runtime.character.templates?.twitterPostTemplate || twitterPostTemplate
|
|
9188
9191
|
});
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
const response = await this.runtime.useModel(ModelTypes5.TEXT_SMALL, {
|
|
9192
|
-
prompt
|
|
9192
|
+
const response = await this.runtime.useModel(ModelTypes5.TEXT_LARGE, {
|
|
9193
|
+
prompt: tweetPrompt
|
|
9193
9194
|
});
|
|
9194
|
-
const
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
if (parsedResponse?.text) {
|
|
9199
|
-
tweetTextForPosting = parsedResponse.text;
|
|
9200
|
-
} else {
|
|
9201
|
-
tweetTextForPosting = rawTweetContent.trim();
|
|
9195
|
+
const jsonResponse = parseJSONObjectFromText(response);
|
|
9196
|
+
if (!jsonResponse || !jsonResponse.text) {
|
|
9197
|
+
logger7.error("Failed to generate valid tweet content");
|
|
9198
|
+
return;
|
|
9202
9199
|
}
|
|
9203
|
-
|
|
9204
|
-
|
|
9200
|
+
const cleanedText = this.cleanupTweetText(jsonResponse.text);
|
|
9201
|
+
const mediaData = [];
|
|
9202
|
+
if (jsonResponse.imagePrompt) {
|
|
9203
|
+
try {
|
|
9204
|
+
const imagePromptMedia = Array.isArray(jsonResponse.imagePrompt) ? jsonResponse.imagePrompt.map((prompt) => ({
|
|
9205
|
+
url: prompt,
|
|
9206
|
+
contentType: "image/png"
|
|
9207
|
+
})) : [{
|
|
9208
|
+
url: jsonResponse.imagePrompt,
|
|
9209
|
+
contentType: "image/png"
|
|
9210
|
+
}];
|
|
9211
|
+
const fetchedMedia = await fetchMediaData(imagePromptMedia);
|
|
9212
|
+
mediaData.push(...fetchedMedia);
|
|
9213
|
+
} catch (error) {
|
|
9214
|
+
logger7.error("Error fetching media for tweet:", error);
|
|
9215
|
+
}
|
|
9205
9216
|
}
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9217
|
+
const tweetId = createUniqueUuid5(this.runtime, `tweet-${Date.now()}`);
|
|
9218
|
+
const memory = {
|
|
9219
|
+
id: tweetId,
|
|
9220
|
+
entityId: this.runtime.agentId,
|
|
9221
|
+
agentId: this.runtime.agentId,
|
|
9222
|
+
roomId: timelineRoomId,
|
|
9223
|
+
content: {
|
|
9224
|
+
text: cleanedText,
|
|
9225
|
+
source: "twitter",
|
|
9226
|
+
channelType: ChannelType5.FEED,
|
|
9227
|
+
thought: jsonResponse.thought || "",
|
|
9228
|
+
plan: jsonResponse.plan || "",
|
|
9229
|
+
type: "post"
|
|
9230
|
+
},
|
|
9231
|
+
createdAt: Date.now()
|
|
9232
|
+
};
|
|
9233
|
+
const callback = async (content) => {
|
|
9234
|
+
try {
|
|
9235
|
+
if (this.isDryRun) {
|
|
9236
|
+
logger7.info(`[DRY RUN] Would post tweet: ${content.text}`);
|
|
9237
|
+
return [];
|
|
9238
|
+
}
|
|
9239
|
+
const result = await this.postToTwitter(content.text, mediaData);
|
|
9240
|
+
if (result) {
|
|
9241
|
+
const postedTweetId = createUniqueUuid5(
|
|
9242
|
+
this.runtime,
|
|
9243
|
+
result.id_str
|
|
9244
|
+
);
|
|
9245
|
+
const postedMemory = {
|
|
9246
|
+
id: postedTweetId,
|
|
9247
|
+
entityId: this.runtime.agentId,
|
|
9248
|
+
agentId: this.runtime.agentId,
|
|
9249
|
+
roomId: timelineRoomId,
|
|
9250
|
+
content: {
|
|
9251
|
+
...content,
|
|
9252
|
+
source: "twitter",
|
|
9253
|
+
channelType: ChannelType5.FEED,
|
|
9254
|
+
type: "post",
|
|
9255
|
+
metadata: {
|
|
9256
|
+
tweetId: result.id_str,
|
|
9257
|
+
postedAt: Date.now()
|
|
9258
|
+
}
|
|
9259
|
+
},
|
|
9260
|
+
createdAt: Date.now()
|
|
9261
|
+
};
|
|
9262
|
+
await this.runtime.getMemoryManager("messages").createMemory(postedMemory);
|
|
9263
|
+
return [postedMemory];
|
|
9264
|
+
}
|
|
9265
|
+
return [];
|
|
9266
|
+
} catch (error) {
|
|
9267
|
+
logger7.error("Error posting tweet:", error);
|
|
9268
|
+
return [];
|
|
9269
|
+
}
|
|
9270
|
+
};
|
|
9271
|
+
this.runtime.emitEvent(["TWITTER_POST_GENERATED", "POST_GENERATED"], {
|
|
9272
|
+
runtime: this.runtime,
|
|
9273
|
+
message: memory,
|
|
9274
|
+
callback,
|
|
9275
|
+
source: "twitter"
|
|
9276
|
+
});
|
|
9277
|
+
} catch (error) {
|
|
9278
|
+
logger7.error("Error generating tweet:", error);
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
/**
|
|
9282
|
+
* Posts content to Twitter
|
|
9283
|
+
* @param {string} text The tweet text to post
|
|
9284
|
+
* @param {MediaData[]} mediaData Optional media to attach to the tweet
|
|
9285
|
+
* @returns {Promise<any>} The result from the Twitter API
|
|
9286
|
+
*/
|
|
9287
|
+
async postToTwitter(text, mediaData = []) {
|
|
9288
|
+
try {
|
|
9289
|
+
const mediaIds = [];
|
|
9290
|
+
if (mediaData && mediaData.length > 0) {
|
|
9291
|
+
for (const media of mediaData) {
|
|
9292
|
+
try {
|
|
9293
|
+
const uploadResult = await this.client.requestQueue.add(
|
|
9294
|
+
() => this.client.twitterClient.post("media/upload", {
|
|
9295
|
+
media_data: Buffer.isBuffer(media.data) ? media.data : Buffer.from(String(media.data).split(",")[1], "base64")
|
|
9296
|
+
})
|
|
9297
|
+
);
|
|
9298
|
+
if (uploadResult && uploadResult.media_id_string) {
|
|
9299
|
+
mediaIds.push(uploadResult.media_id_string);
|
|
9300
|
+
}
|
|
9301
|
+
} catch (error) {
|
|
9302
|
+
logger7.error("Error uploading media:", error);
|
|
9303
|
+
}
|
|
9213
9304
|
}
|
|
9214
9305
|
}
|
|
9215
|
-
|
|
9216
|
-
|
|
9306
|
+
const tweetParams = {
|
|
9307
|
+
status: text.substring(0, 280)
|
|
9308
|
+
// Twitter's character limit
|
|
9309
|
+
};
|
|
9310
|
+
if (mediaIds.length > 0) {
|
|
9311
|
+
tweetParams.media_ids = mediaIds.join(",");
|
|
9217
9312
|
}
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
280 - 1
|
|
9313
|
+
const result = await this.client.requestQueue.add(
|
|
9314
|
+
() => this.client.twitterClient.post("statuses/update", tweetParams)
|
|
9221
9315
|
);
|
|
9222
|
-
|
|
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);
|
|
9243
|
-
}
|
|
9316
|
+
return result;
|
|
9244
9317
|
} catch (error) {
|
|
9245
|
-
logger7.error("Error
|
|
9318
|
+
logger7.error("Error posting to Twitter:", error);
|
|
9319
|
+
throw error;
|
|
9246
9320
|
}
|
|
9247
9321
|
}
|
|
9322
|
+
/**
|
|
9323
|
+
* Cleans up a tweet text by removing quotes and fixing newlines
|
|
9324
|
+
*/
|
|
9325
|
+
cleanupTweetText(text) {
|
|
9326
|
+
let cleanedText = text.replace(/^['"](.*)['"]$/, "$1");
|
|
9327
|
+
cleanedText = cleanedText.replaceAll(/\\n/g, "\n\n");
|
|
9328
|
+
if (cleanedText.length > 280) {
|
|
9329
|
+
cleanedText = truncateToCompleteSentence(cleanedText, 280);
|
|
9330
|
+
}
|
|
9331
|
+
return cleanedText;
|
|
9332
|
+
}
|
|
9248
9333
|
async stop() {
|
|
9249
9334
|
}
|
|
9250
9335
|
};
|
|
@@ -9256,13 +9341,6 @@ import {
|
|
|
9256
9341
|
logger as logger8,
|
|
9257
9342
|
stringToUuid as stringToUuid2
|
|
9258
9343
|
} from "@elizaos/core";
|
|
9259
|
-
|
|
9260
|
-
// src/types.ts
|
|
9261
|
-
var ServiceTypes2 = {
|
|
9262
|
-
TWITTER: "twitter"
|
|
9263
|
-
};
|
|
9264
|
-
|
|
9265
|
-
// src/tests.ts
|
|
9266
9344
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
9267
9345
|
var TEST_IMAGE = {
|
|
9268
9346
|
id: "mock-image-id",
|
|
@@ -9275,14 +9353,13 @@ var TEST_IMAGE = {
|
|
|
9275
9353
|
alt_text: "mock image"
|
|
9276
9354
|
};
|
|
9277
9355
|
var TwitterTestSuite = class {
|
|
9278
|
-
name = "twitter";
|
|
9279
|
-
twitterClient = null;
|
|
9280
|
-
tests;
|
|
9281
9356
|
/**
|
|
9282
9357
|
* Constructor for TestSuite class.
|
|
9283
9358
|
* Initializes an array of test functions to be executed.
|
|
9284
9359
|
*/
|
|
9285
9360
|
constructor() {
|
|
9361
|
+
this.name = "twitter";
|
|
9362
|
+
this.twitterClient = null;
|
|
9286
9363
|
this.tests = [
|
|
9287
9364
|
{
|
|
9288
9365
|
name: "Initialize Twitter Client",
|
|
@@ -9553,11 +9630,6 @@ var TwitterTestSuite = class {
|
|
|
9553
9630
|
|
|
9554
9631
|
// src/index.ts
|
|
9555
9632
|
var TwitterClientInstance = class {
|
|
9556
|
-
client;
|
|
9557
|
-
post;
|
|
9558
|
-
interaction;
|
|
9559
|
-
space;
|
|
9560
|
-
service;
|
|
9561
9633
|
constructor(runtime, state) {
|
|
9562
9634
|
this.client = new ClientBase(runtime, state);
|
|
9563
9635
|
this.post = new TwitterPostClient(this.client, runtime, state);
|
|
@@ -9572,11 +9644,12 @@ var TwitterClientInstance = class {
|
|
|
9572
9644
|
this.service = TwitterService.getInstance();
|
|
9573
9645
|
}
|
|
9574
9646
|
};
|
|
9575
|
-
var
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9647
|
+
var _TwitterService = class _TwitterService extends Service {
|
|
9648
|
+
constructor() {
|
|
9649
|
+
super(...arguments);
|
|
9650
|
+
this.capabilityDescription = "The agent is able to send and receive messages on twitter";
|
|
9651
|
+
this.clients = /* @__PURE__ */ new Map();
|
|
9652
|
+
}
|
|
9580
9653
|
static getInstance() {
|
|
9581
9654
|
if (!_TwitterService.instance) {
|
|
9582
9655
|
_TwitterService.instance = new _TwitterService();
|
|
@@ -9606,6 +9679,7 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9606
9679
|
client.interaction.start();
|
|
9607
9680
|
}
|
|
9608
9681
|
this.clients.set(this.getClientKey(clientId, runtime.agentId), client);
|
|
9682
|
+
await this.emitServerJoinedEvent(runtime, client);
|
|
9609
9683
|
logger9.info(`Created Twitter client for ${clientId}`);
|
|
9610
9684
|
return client;
|
|
9611
9685
|
} catch (error) {
|
|
@@ -9613,6 +9687,83 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9613
9687
|
throw error;
|
|
9614
9688
|
}
|
|
9615
9689
|
}
|
|
9690
|
+
/**
|
|
9691
|
+
* Emits a standardized WORLD_JOINED event for Twitter
|
|
9692
|
+
* @param runtime The agent runtime
|
|
9693
|
+
* @param client The Twitter client instance
|
|
9694
|
+
*/
|
|
9695
|
+
async emitServerJoinedEvent(runtime, client) {
|
|
9696
|
+
try {
|
|
9697
|
+
if (!client.client.profile) {
|
|
9698
|
+
logger9.warn("Twitter profile not available yet, can't emit WORLD_JOINED event");
|
|
9699
|
+
return;
|
|
9700
|
+
}
|
|
9701
|
+
const profile = client.client.profile;
|
|
9702
|
+
const twitterId = profile.id;
|
|
9703
|
+
const username = profile.username;
|
|
9704
|
+
const worldId = createUniqueUuid7(runtime, twitterId);
|
|
9705
|
+
const world = {
|
|
9706
|
+
id: worldId,
|
|
9707
|
+
name: `${username}'s Twitter`,
|
|
9708
|
+
agentId: runtime.agentId,
|
|
9709
|
+
serverId: twitterId,
|
|
9710
|
+
metadata: {
|
|
9711
|
+
ownership: { ownerId: twitterId },
|
|
9712
|
+
roles: {
|
|
9713
|
+
[twitterId]: Role.OWNER
|
|
9714
|
+
},
|
|
9715
|
+
twitter: {
|
|
9716
|
+
username,
|
|
9717
|
+
id: twitterId
|
|
9718
|
+
}
|
|
9719
|
+
}
|
|
9720
|
+
};
|
|
9721
|
+
const homeTimelineRoomId = createUniqueUuid7(runtime, `${twitterId}-home`);
|
|
9722
|
+
const homeTimelineRoom = {
|
|
9723
|
+
id: homeTimelineRoomId,
|
|
9724
|
+
name: `${username}'s Timeline`,
|
|
9725
|
+
source: "twitter",
|
|
9726
|
+
type: ChannelType6.FEED,
|
|
9727
|
+
channelId: `${twitterId}-home`,
|
|
9728
|
+
serverId: twitterId,
|
|
9729
|
+
worldId
|
|
9730
|
+
};
|
|
9731
|
+
const mentionsRoomId = createUniqueUuid7(runtime, `${twitterId}-mentions`);
|
|
9732
|
+
const mentionsRoom = {
|
|
9733
|
+
id: mentionsRoomId,
|
|
9734
|
+
name: `${username}'s Mentions`,
|
|
9735
|
+
source: "twitter",
|
|
9736
|
+
type: ChannelType6.GROUP,
|
|
9737
|
+
channelId: `${twitterId}-mentions`,
|
|
9738
|
+
serverId: twitterId,
|
|
9739
|
+
worldId
|
|
9740
|
+
};
|
|
9741
|
+
const twitterUserId = createUniqueUuid7(runtime, twitterId);
|
|
9742
|
+
const twitterUser = {
|
|
9743
|
+
id: twitterUserId,
|
|
9744
|
+
names: [profile.screenName || username],
|
|
9745
|
+
agentId: runtime.agentId,
|
|
9746
|
+
metadata: {
|
|
9747
|
+
twitter: {
|
|
9748
|
+
id: twitterId,
|
|
9749
|
+
username,
|
|
9750
|
+
screenName: profile.screenName || username,
|
|
9751
|
+
name: profile.screenName || username
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
};
|
|
9755
|
+
runtime.emitEvent(["TWITTER_WORLD_JOINED" /* WORLD_JOINED */, EventTypes3.WORLD_JOINED], {
|
|
9756
|
+
runtime,
|
|
9757
|
+
world,
|
|
9758
|
+
rooms: [homeTimelineRoom, mentionsRoom],
|
|
9759
|
+
users: [twitterUser],
|
|
9760
|
+
source: "twitter"
|
|
9761
|
+
});
|
|
9762
|
+
logger9.info(`Emitted WORLD_JOINED event for Twitter account ${username}`);
|
|
9763
|
+
} catch (error) {
|
|
9764
|
+
logger9.error("Failed to emit WORLD_JOINED event for Twitter:", error);
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9616
9767
|
getClient(clientId, agentId) {
|
|
9617
9768
|
return this.clients.get(this.getClientKey(clientId, agentId));
|
|
9618
9769
|
}
|
|
@@ -9672,6 +9823,8 @@ var TwitterService = class _TwitterService extends Service {
|
|
|
9672
9823
|
return `${clientId}-${agentId}`;
|
|
9673
9824
|
}
|
|
9674
9825
|
};
|
|
9826
|
+
_TwitterService.serviceType = TWITTER_SERVICE_NAME;
|
|
9827
|
+
var TwitterService = _TwitterService;
|
|
9675
9828
|
var twitterPlugin = {
|
|
9676
9829
|
name: TWITTER_SERVICE_NAME,
|
|
9677
9830
|
description: "Twitter client with per-server instance management",
|