@elizaos/plugin-farcaster 1.0.5 → 1.7.0
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 +42 -1
- package/dist/__tests__/e2e/scenarios.d.ts +4 -0
- package/dist/__tests__/e2e/scenarios.d.ts.map +1 -0
- package/dist/__tests__/suite.d.ts +117 -0
- package/dist/__tests__/suite.d.ts.map +1 -0
- package/dist/__tests__/test-utils.d.ts +29 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/__tests__/types.d.ts +88 -0
- package/dist/__tests__/types.d.ts.map +1 -0
- package/dist/actions/index.d.ts +4 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/replyCast.d.ts +3 -0
- package/dist/actions/replyCast.d.ts.map +1 -0
- package/dist/actions/sendCast.d.ts +3 -0
- package/dist/actions/sendCast.d.ts.map +1 -0
- package/dist/client.d.ts +26 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/common/asyncqueue.d.ts +13 -0
- package/dist/common/asyncqueue.d.ts.map +1 -0
- package/dist/common/callbacks.d.ts +14 -0
- package/dist/common/callbacks.d.ts.map +1 -0
- package/dist/common/config.d.ts +15 -0
- package/dist/common/config.d.ts.map +1 -0
- package/dist/common/constants.d.ts +9 -0
- package/dist/common/constants.d.ts.map +1 -0
- package/dist/common/prompts.d.ts +5 -0
- package/dist/common/prompts.d.ts.map +1 -0
- package/dist/common/types.d.ts +80 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/common/utils.d.ts +24 -0
- package/dist/common/utils.d.ts.map +1 -0
- package/dist/events/messageHandler.d.ts +29 -0
- package/dist/events/messageHandler.d.ts.map +1 -0
- package/dist/index.d.ts +7 -500
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1050 -739
- package/dist/index.js.map +35 -1
- package/dist/managers/agent.d.ts +21 -0
- package/dist/managers/agent.d.ts.map +1 -0
- package/dist/managers/interaction-processor.d.ts +12 -0
- package/dist/managers/interaction-processor.d.ts.map +1 -0
- package/dist/managers/interaction-source.d.ts +50 -0
- package/dist/managers/interaction-source.d.ts.map +1 -0
- package/dist/managers/interactions.d.ts +49 -0
- package/dist/managers/interactions.d.ts.map +1 -0
- package/dist/managers/post.d.ts +24 -0
- package/dist/managers/post.d.ts.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/profileProvider.d.ts +3 -0
- package/dist/providers/profileProvider.d.ts.map +1 -0
- package/dist/providers/threadProvider.d.ts +3 -0
- package/dist/providers/threadProvider.d.ts.map +1 -0
- package/dist/providers/timelineProvider.d.ts +3 -0
- package/dist/providers/timelineProvider.d.ts.map +1 -0
- package/dist/routes/webhook.d.ts +3 -0
- package/dist/routes/webhook.d.ts.map +1 -0
- package/dist/service.d.ts +31 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/services/CastService.d.ts +142 -0
- package/dist/services/CastService.d.ts.map +1 -0
- package/dist/services/MessageService.d.ts +50 -0
- package/dist/services/MessageService.d.ts.map +1 -0
- package/package.json +5 -6
- package/dist/chunk-FNDASAYG.js +0 -83
- package/dist/chunk-FNDASAYG.js.map +0 -1
- package/dist/chunk-IOTLJXKN.js +0 -72
- package/dist/chunk-IOTLJXKN.js.map +0 -1
- package/dist/chunk-OAXQ6Z2Q.js +0 -105
- package/dist/chunk-OAXQ6Z2Q.js.map +0 -1
- package/dist/chunk-Y2URJ4EZ.js +0 -21
- package/dist/chunk-Y2URJ4EZ.js.map +0 -1
- package/dist/profileProvider-TNRU42OO.js +0 -8
- package/dist/profileProvider-TNRU42OO.js.map +0 -1
- package/dist/sendCast-OW6DBKQB.js +0 -8
- package/dist/sendCast-OW6DBKQB.js.map +0 -1
- package/dist/timelineProvider-GPRPFEVJ.js +0 -8
- package/dist/timelineProvider-GPRPFEVJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,37 +1,287 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
+
|
|
13
|
+
// src/common/constants.ts
|
|
14
|
+
var FARCASTER_SERVICE_NAME = "farcaster", FARCASTER_SOURCE = "farcaster", DEFAULT_MAX_CAST_LENGTH = 320, DEFAULT_POLL_INTERVAL = 120, DEFAULT_CAST_INTERVAL_MIN = 90, DEFAULT_CAST_INTERVAL_MAX = 180, DEFAULT_CAST_CACHE_TTL, DEFAULT_CAST_CACHE_SIZE = 9000;
|
|
15
|
+
var init_constants = __esm(() => {
|
|
16
|
+
DEFAULT_CAST_CACHE_TTL = 1000 * 30 * 60;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// src/actions/sendCast.ts
|
|
20
|
+
var exports_sendCast = {};
|
|
21
|
+
__export(exports_sendCast, {
|
|
22
|
+
sendCastAction: () => sendCastAction
|
|
23
|
+
});
|
|
1
24
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
logger as logger8,
|
|
26
|
+
createUniqueUuid as createUniqueUuid5
|
|
27
|
+
} from "@elizaos/core";
|
|
28
|
+
var sendCastAction;
|
|
29
|
+
var init_sendCast = __esm(() => {
|
|
30
|
+
init_constants();
|
|
31
|
+
sendCastAction = {
|
|
32
|
+
name: "SEND_CAST",
|
|
33
|
+
description: "Posts a cast (message) on Farcaster",
|
|
34
|
+
examples: [
|
|
35
|
+
[
|
|
36
|
+
{
|
|
37
|
+
name: "user",
|
|
38
|
+
content: { text: "Can you post about the new ElizaOS features on Farcaster?" }
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "assistant",
|
|
42
|
+
content: {
|
|
43
|
+
text: "I'll post about the new ElizaOS features on Farcaster now.",
|
|
44
|
+
actions: ["SEND_CAST"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
name: "user",
|
|
51
|
+
content: { text: "Share on Farcaster that we just launched version 2.0!" }
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "assistant",
|
|
55
|
+
content: {
|
|
56
|
+
text: "I'll share the version 2.0 launch announcement on Farcaster.",
|
|
57
|
+
actions: ["SEND_CAST"]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
],
|
|
62
|
+
validate: async (runtime, message) => {
|
|
63
|
+
const text = message.content.text?.toLowerCase() || "";
|
|
64
|
+
const keywords = ["post", "cast", "share", "announce", "farcaster", "tweet"];
|
|
65
|
+
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
66
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
67
|
+
const isServiceAvailable = !!service?.getCastService(runtime.agentId);
|
|
68
|
+
return hasKeyword && isServiceAvailable;
|
|
69
|
+
},
|
|
70
|
+
handler: async (runtime, message, state) => {
|
|
71
|
+
try {
|
|
72
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
73
|
+
const postService = service?.getCastService(runtime.agentId);
|
|
74
|
+
if (!postService) {
|
|
75
|
+
runtime.logger.error("[SEND_CAST] PostService not available");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
let castContent = "";
|
|
79
|
+
if (state?.castContent) {
|
|
80
|
+
castContent = state.castContent;
|
|
81
|
+
} else {
|
|
82
|
+
const prompt = `Based on this request: "${message.content.text}", generate a concise Farcaster cast (max 320 characters). Be engaging and use appropriate hashtags if relevant.`;
|
|
83
|
+
const response = await runtime.useModel("text_large", { prompt });
|
|
84
|
+
castContent = typeof response === "string" ? response : response.text || "";
|
|
85
|
+
}
|
|
86
|
+
if (castContent.length > 320) {
|
|
87
|
+
castContent = castContent.substring(0, 317) + "...";
|
|
88
|
+
}
|
|
89
|
+
const cast = await postService.createCast({
|
|
90
|
+
agentId: runtime.agentId,
|
|
91
|
+
roomId: createUniqueUuid5(runtime, "farcaster-timeline"),
|
|
92
|
+
text: castContent
|
|
93
|
+
});
|
|
94
|
+
logger8.info(`[SEND_CAST] Successfully posted cast: ${cast.id}`);
|
|
95
|
+
await runtime.createMemory({
|
|
96
|
+
agentId: runtime.agentId,
|
|
97
|
+
roomId: cast.roomId,
|
|
98
|
+
entityId: runtime.agentId,
|
|
99
|
+
content: {
|
|
100
|
+
text: castContent,
|
|
101
|
+
source: "farcaster",
|
|
102
|
+
metadata: {
|
|
103
|
+
castHash: cast.metadata?.castHash,
|
|
104
|
+
action: "SEND_CAST"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
createdAt: cast.timestamp
|
|
108
|
+
}, "messages");
|
|
109
|
+
} catch (error) {
|
|
110
|
+
runtime.logger.error("[SEND_CAST] Error posting cast:", typeof error === "string" ? error : error.message);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// src/providers/profileProvider.ts
|
|
118
|
+
var exports_profileProvider = {};
|
|
119
|
+
__export(exports_profileProvider, {
|
|
120
|
+
farcasterProfileProvider: () => farcasterProfileProvider
|
|
121
|
+
});
|
|
122
|
+
var farcasterProfileProvider;
|
|
123
|
+
var init_profileProvider = __esm(() => {
|
|
124
|
+
init_constants();
|
|
125
|
+
farcasterProfileProvider = {
|
|
126
|
+
name: "farcasterProfile",
|
|
127
|
+
description: "Provides information about the agent's Farcaster profile",
|
|
128
|
+
get: async (runtime, message, state) => {
|
|
129
|
+
try {
|
|
130
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
131
|
+
const managers = service?.getActiveManagers();
|
|
132
|
+
if (!managers || managers.size === 0) {
|
|
133
|
+
runtime.logger.debug("[FarcasterProfileProvider] No managers available");
|
|
134
|
+
return {
|
|
135
|
+
text: "Farcaster profile not available.",
|
|
136
|
+
data: { available: false }
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const manager = managers.get(runtime.agentId);
|
|
140
|
+
if (!manager) {
|
|
141
|
+
runtime.logger.debug("[FarcasterProfileProvider] No manager for this agent");
|
|
142
|
+
return {
|
|
143
|
+
text: "Farcaster profile not available for this agent.",
|
|
144
|
+
data: { available: false }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const fid = parseInt(runtime.getSetting("FARCASTER_FID"), 10);
|
|
148
|
+
if (!fid || isNaN(fid)) {
|
|
149
|
+
runtime.logger.warn("[FarcasterProfileProvider] Invalid or missing FARCASTER_FID");
|
|
150
|
+
return {
|
|
151
|
+
text: "Invalid Farcaster FID configured.",
|
|
152
|
+
data: { available: false, error: "Invalid FID" }
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const profile = await manager.client.getProfile(fid);
|
|
157
|
+
return {
|
|
158
|
+
text: `Your Farcaster profile: @${profile.username} (FID: ${profile.fid}). ${profile.name ? `Display name: ${profile.name}` : ""}`,
|
|
159
|
+
data: {
|
|
160
|
+
available: true,
|
|
161
|
+
fid: profile.fid,
|
|
162
|
+
username: profile.username,
|
|
163
|
+
name: profile.name,
|
|
164
|
+
pfp: profile.pfp
|
|
165
|
+
},
|
|
166
|
+
values: {
|
|
167
|
+
fid: profile.fid,
|
|
168
|
+
username: profile.username
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
} catch (error) {
|
|
172
|
+
runtime.logger.error("[FarcasterProfileProvider] Error fetching profile:", typeof error === "string" ? error : error.message);
|
|
173
|
+
return {
|
|
174
|
+
text: "Unable to fetch Farcaster profile at this time.",
|
|
175
|
+
data: { available: false, error: "Fetch failed" }
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
runtime.logger.error("[FarcasterProfileProvider] Error:", typeof error === "string" ? error : error.message);
|
|
180
|
+
return {
|
|
181
|
+
text: "Farcaster service is not available.",
|
|
182
|
+
data: { available: false }
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// src/providers/timelineProvider.ts
|
|
190
|
+
var exports_timelineProvider = {};
|
|
191
|
+
__export(exports_timelineProvider, {
|
|
192
|
+
farcasterTimelineProvider: () => farcasterTimelineProvider
|
|
193
|
+
});
|
|
194
|
+
function getTimeAgo(date) {
|
|
195
|
+
const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000);
|
|
196
|
+
if (seconds < 60)
|
|
197
|
+
return "just now";
|
|
198
|
+
if (seconds < 3600)
|
|
199
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
200
|
+
if (seconds < 86400)
|
|
201
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
202
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
203
|
+
}
|
|
204
|
+
var farcasterTimelineProvider;
|
|
205
|
+
var init_timelineProvider = __esm(() => {
|
|
206
|
+
init_constants();
|
|
207
|
+
farcasterTimelineProvider = {
|
|
208
|
+
name: "farcasterTimeline",
|
|
209
|
+
description: "Provides recent casts from the agent's Farcaster timeline",
|
|
210
|
+
get: async (runtime, message, state) => {
|
|
211
|
+
try {
|
|
212
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
213
|
+
const castService = service?.getCastService(runtime.agentId);
|
|
214
|
+
if (!castService) {
|
|
215
|
+
return {
|
|
216
|
+
text: "Farcaster timeline not available.",
|
|
217
|
+
data: { available: false }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const casts = await castService.getCasts({
|
|
221
|
+
agentId: runtime.agentId,
|
|
222
|
+
limit: 5
|
|
223
|
+
});
|
|
224
|
+
if (!casts || casts.length === 0) {
|
|
225
|
+
return {
|
|
226
|
+
text: "No recent casts in your timeline.",
|
|
227
|
+
data: {
|
|
228
|
+
available: true,
|
|
229
|
+
casts: [],
|
|
230
|
+
count: 0
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const formattedCasts = casts.map((cast, index) => {
|
|
235
|
+
const timeAgo = getTimeAgo(new Date(cast.timestamp));
|
|
236
|
+
return `${index + 1}. @${cast.username} (${timeAgo}): ${cast.text}`;
|
|
237
|
+
}).join(`
|
|
238
|
+
`);
|
|
239
|
+
return {
|
|
240
|
+
text: `Recent casts from your timeline:
|
|
241
|
+
${formattedCasts}`,
|
|
242
|
+
data: {
|
|
243
|
+
available: true,
|
|
244
|
+
casts: casts.map((c) => ({
|
|
245
|
+
id: c.id,
|
|
246
|
+
username: c.username,
|
|
247
|
+
text: c.text,
|
|
248
|
+
timestamp: c.timestamp,
|
|
249
|
+
castHash: c.metadata?.castHash
|
|
250
|
+
})),
|
|
251
|
+
count: casts.length
|
|
252
|
+
},
|
|
253
|
+
values: {
|
|
254
|
+
latestCastHash: casts[0]?.metadata?.castHash,
|
|
255
|
+
latestCastText: casts[0]?.text
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
} catch (error) {
|
|
259
|
+
runtime.logger.error("[FarcasterTimelineProvider] Error:", typeof error === "string" ? error : error.message);
|
|
260
|
+
return {
|
|
261
|
+
text: "Unable to fetch Farcaster timeline.",
|
|
262
|
+
data: { available: false, error: error instanceof Error ? error.message : "Unknown error" }
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
});
|
|
20
268
|
|
|
21
269
|
// src/service.ts
|
|
22
|
-
|
|
270
|
+
init_constants();
|
|
271
|
+
import { logger as logger7, Service } from "@elizaos/core";
|
|
23
272
|
|
|
24
273
|
// src/managers/agent.ts
|
|
25
274
|
import { logger as logger4 } from "@elizaos/core";
|
|
26
275
|
import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk";
|
|
27
276
|
|
|
28
277
|
// src/client.ts
|
|
278
|
+
init_constants();
|
|
29
279
|
import { elizaLogger } from "@elizaos/core";
|
|
30
280
|
import { isApiErrorResponse } from "@neynar/nodejs-sdk";
|
|
31
|
-
import { CastParamType } from "@neynar/nodejs-sdk/build/api/index.js";
|
|
32
281
|
import { LRUCache } from "lru-cache";
|
|
33
282
|
|
|
34
283
|
// src/common/utils.ts
|
|
284
|
+
init_constants();
|
|
35
285
|
import { stringToUuid } from "@elizaos/core";
|
|
36
286
|
var MAX_CAST_LENGTH = 1024;
|
|
37
287
|
function castId({ hash, agentId }) {
|
|
@@ -41,14 +291,21 @@ function castUuid(props) {
|
|
|
41
291
|
return stringToUuid(castId(props));
|
|
42
292
|
}
|
|
43
293
|
function splitPostContent(content, maxLength = MAX_CAST_LENGTH) {
|
|
44
|
-
const paragraphs = content.split(
|
|
294
|
+
const paragraphs = content.split(`
|
|
295
|
+
|
|
296
|
+
`).map((p) => p.trim());
|
|
45
297
|
const posts = [];
|
|
46
298
|
let currentCast = "";
|
|
47
299
|
for (const paragraph of paragraphs) {
|
|
48
|
-
if (!paragraph)
|
|
49
|
-
|
|
300
|
+
if (!paragraph)
|
|
301
|
+
continue;
|
|
302
|
+
if ((currentCast + `
|
|
303
|
+
|
|
304
|
+
` + paragraph).trim().length <= maxLength) {
|
|
50
305
|
if (currentCast) {
|
|
51
|
-
currentCast +=
|
|
306
|
+
currentCast += `
|
|
307
|
+
|
|
308
|
+
` + paragraph;
|
|
52
309
|
} else {
|
|
53
310
|
currentCast = paragraph;
|
|
54
311
|
}
|
|
@@ -116,18 +373,17 @@ function lastCastCacheKey(fid) {
|
|
|
116
373
|
return `farcaster/${fid}/lastCast`;
|
|
117
374
|
}
|
|
118
375
|
function neynarCastToCast(neynarCast) {
|
|
119
|
-
var _a;
|
|
120
376
|
return {
|
|
121
377
|
hash: neynarCast.hash,
|
|
122
378
|
authorFid: neynarCast.author.fid,
|
|
123
379
|
text: neynarCast.text,
|
|
124
|
-
threadId: neynarCast.thread_hash ??
|
|
380
|
+
threadId: neynarCast.thread_hash ?? undefined,
|
|
125
381
|
profile: {
|
|
126
382
|
fid: neynarCast.author.fid,
|
|
127
383
|
name: neynarCast.author.display_name || "anon",
|
|
128
384
|
username: neynarCast.author.username
|
|
129
385
|
},
|
|
130
|
-
...neynarCast.parent_hash &&
|
|
386
|
+
...neynarCast.parent_hash && neynarCast.parent_author?.fid ? {
|
|
131
387
|
inReplyTo: {
|
|
132
388
|
hash: neynarCast.parent_hash,
|
|
133
389
|
fid: neynarCast.parent_author.fid
|
|
@@ -145,7 +401,7 @@ function createCastMemory({
|
|
|
145
401
|
const inReplyTo = cast.inReplyTo ? castUuid({
|
|
146
402
|
hash: cast.inReplyTo.hash,
|
|
147
403
|
agentId: runtime.agentId
|
|
148
|
-
}) :
|
|
404
|
+
}) : undefined;
|
|
149
405
|
return {
|
|
150
406
|
id: castUuid({
|
|
151
407
|
hash: cast.hash,
|
|
@@ -164,14 +420,6 @@ function createCastMemory({
|
|
|
164
420
|
roomId
|
|
165
421
|
};
|
|
166
422
|
}
|
|
167
|
-
function formatCastTimestamp(timestamp) {
|
|
168
|
-
return timestamp.toLocaleString("en-US", {
|
|
169
|
-
hour: "2-digit",
|
|
170
|
-
minute: "2-digit",
|
|
171
|
-
month: "short",
|
|
172
|
-
day: "numeric"
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
423
|
|
|
176
424
|
// src/client.ts
|
|
177
425
|
var castCache = new LRUCache({
|
|
@@ -179,11 +427,11 @@ var castCache = new LRUCache({
|
|
|
179
427
|
ttl: DEFAULT_CAST_CACHE_TTL
|
|
180
428
|
});
|
|
181
429
|
var profileCache = new LRUCache({
|
|
182
|
-
max:
|
|
183
|
-
ttl:
|
|
184
|
-
// 15 minutes
|
|
430
|
+
max: 1000,
|
|
431
|
+
ttl: 1000 * 60 * 15
|
|
185
432
|
});
|
|
186
|
-
|
|
433
|
+
|
|
434
|
+
class FarcasterClient {
|
|
187
435
|
neynar;
|
|
188
436
|
signerUuid;
|
|
189
437
|
constructor(opts) {
|
|
@@ -211,7 +459,7 @@ var FarcasterClient = class {
|
|
|
211
459
|
const result = await this.neynar.publishCast({
|
|
212
460
|
signerUuid: this.signerUuid,
|
|
213
461
|
text: cast,
|
|
214
|
-
parent: parentCastId
|
|
462
|
+
parent: parentCastId?.hash
|
|
215
463
|
});
|
|
216
464
|
if (result.success) {
|
|
217
465
|
return this.getCast(result.cast.hash);
|
|
@@ -219,10 +467,10 @@ var FarcasterClient = class {
|
|
|
219
467
|
throw new Error(`[Farcaster] Error publishing [${cast}] parentCastId: [${parentCastId}]`);
|
|
220
468
|
} catch (err) {
|
|
221
469
|
if (isApiErrorResponse(err)) {
|
|
222
|
-
elizaLogger.error(
|
|
470
|
+
elizaLogger.error(`Neynar error: ${JSON.stringify(err.response.data)}`);
|
|
223
471
|
throw err.response.data;
|
|
224
472
|
} else {
|
|
225
|
-
elizaLogger.error(
|
|
473
|
+
elizaLogger.error(`Error: ${JSON.stringify(err)}`);
|
|
226
474
|
throw err;
|
|
227
475
|
}
|
|
228
476
|
}
|
|
@@ -232,8 +480,7 @@ var FarcasterClient = class {
|
|
|
232
480
|
if (cachedCast) {
|
|
233
481
|
return cachedCast;
|
|
234
482
|
}
|
|
235
|
-
const
|
|
236
|
-
const response = await this.neynar.lookupCastByHashOrWarpcastUrl(params);
|
|
483
|
+
const response = await this.neynar.lookupCastByHashOrUrl({ identifier: castHash, type: "hash" });
|
|
237
484
|
castCache.set(castHash, response.cast);
|
|
238
485
|
return response.cast;
|
|
239
486
|
}
|
|
@@ -275,12 +522,11 @@ var FarcasterClient = class {
|
|
|
275
522
|
profileCache.set(fid, profile);
|
|
276
523
|
return profile;
|
|
277
524
|
} catch (error) {
|
|
278
|
-
elizaLogger.error(
|
|
525
|
+
elizaLogger.error(`Error fetching profile: ${JSON.stringify(error)}`);
|
|
279
526
|
throw error;
|
|
280
527
|
}
|
|
281
528
|
}
|
|
282
529
|
async getTimeline(request) {
|
|
283
|
-
var _a;
|
|
284
530
|
const timeline = [];
|
|
285
531
|
const response = await this.neynar.fetchCastsForUser({
|
|
286
532
|
fid: request.fid,
|
|
@@ -290,7 +536,7 @@ var FarcasterClient = class {
|
|
|
290
536
|
castCache.set(cast.hash, cast);
|
|
291
537
|
timeline.push(neynarCastToCast(cast));
|
|
292
538
|
}
|
|
293
|
-
const nextCursor =
|
|
539
|
+
const nextCursor = response.next?.cursor ?? undefined;
|
|
294
540
|
return {
|
|
295
541
|
timeline,
|
|
296
542
|
cursor: nextCursor
|
|
@@ -300,78 +546,10 @@ var FarcasterClient = class {
|
|
|
300
546
|
profileCache.clear();
|
|
301
547
|
castCache.clear();
|
|
302
548
|
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// src/managers/interactions.ts
|
|
306
|
-
import {
|
|
307
|
-
ChannelType,
|
|
308
|
-
composePrompt,
|
|
309
|
-
createUniqueUuid,
|
|
310
|
-
EventType,
|
|
311
|
-
logger as logger2,
|
|
312
|
-
ModelType
|
|
313
|
-
} from "@elizaos/core";
|
|
549
|
+
}
|
|
314
550
|
|
|
315
|
-
// src/
|
|
316
|
-
|
|
317
|
-
queue = [];
|
|
318
|
-
running = 0;
|
|
319
|
-
emptyListeners = [];
|
|
320
|
-
maxConcurrent;
|
|
321
|
-
constructor(maxConcurrent = 1) {
|
|
322
|
-
this.maxConcurrent = maxConcurrent;
|
|
323
|
-
}
|
|
324
|
-
async submit(work) {
|
|
325
|
-
return new Promise((resolve, reject) => {
|
|
326
|
-
this.queue.push(async () => {
|
|
327
|
-
try {
|
|
328
|
-
resolve(await work());
|
|
329
|
-
} catch (err) {
|
|
330
|
-
reject(err);
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
void this.doNextWork();
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
async doNextWork() {
|
|
337
|
-
if (this.running >= this.maxConcurrent) {
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
const work = this.queue.shift();
|
|
341
|
-
if (!work) {
|
|
342
|
-
this.checkIfEmptyAndNotify();
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
this.running++;
|
|
346
|
-
try {
|
|
347
|
-
await work();
|
|
348
|
-
} catch (err) {
|
|
349
|
-
} finally {
|
|
350
|
-
this.running--;
|
|
351
|
-
void this.doNextWork();
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
async size() {
|
|
355
|
-
return this.queue.length;
|
|
356
|
-
}
|
|
357
|
-
async waitUntilFinished() {
|
|
358
|
-
return new Promise((resolve) => {
|
|
359
|
-
if (this.queue.length === 0 && this.running === 0) {
|
|
360
|
-
resolve();
|
|
361
|
-
} else {
|
|
362
|
-
this.emptyListeners.push(resolve);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
checkIfEmptyAndNotify() {
|
|
367
|
-
if (this.queue.length === 0 && this.running === 0) {
|
|
368
|
-
while (this.emptyListeners.length) {
|
|
369
|
-
const listener = this.emptyListeners.shift();
|
|
370
|
-
if (listener) listener();
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
};
|
|
551
|
+
// src/managers/post.ts
|
|
552
|
+
import { createUniqueUuid, EventType } from "@elizaos/core";
|
|
375
553
|
|
|
376
554
|
// src/common/callbacks.ts
|
|
377
555
|
import { logger } from "@elizaos/core";
|
|
@@ -396,7 +574,7 @@ function standardCastHandlerCallback({
|
|
|
396
574
|
return [];
|
|
397
575
|
}
|
|
398
576
|
const memories = [];
|
|
399
|
-
for (let i = 0;
|
|
577
|
+
for (let i = 0;i < casts.length; i++) {
|
|
400
578
|
const cast = casts[i];
|
|
401
579
|
logger.success(`[Farcaster] Published cast ${cast.hash}`);
|
|
402
580
|
const memory = createCastMemory({
|
|
@@ -416,7 +594,7 @@ function standardCastHandlerCallback({
|
|
|
416
594
|
}
|
|
417
595
|
return memories;
|
|
418
596
|
} catch (error) {
|
|
419
|
-
logger.error("[Farcaster] Error posting cast:", error);
|
|
597
|
+
runtime.logger.error("[Farcaster] Error posting cast:", typeof error === "string" ? error : error.message);
|
|
420
598
|
if (onError) {
|
|
421
599
|
await onError(error);
|
|
422
600
|
}
|
|
@@ -426,27 +604,18 @@ function standardCastHandlerCallback({
|
|
|
426
604
|
return callback;
|
|
427
605
|
}
|
|
428
606
|
|
|
429
|
-
// src/
|
|
430
|
-
|
|
431
|
-
return `ID: ${cast.hash}
|
|
432
|
-
From: ${cast.profile.name} (@${cast.profile.username})${cast.profile.username})${cast.inReplyTo ? `
|
|
433
|
-
In reply to: ${cast.inReplyTo.fid}` : ""}
|
|
434
|
-
Text: ${cast.text}`;
|
|
435
|
-
};
|
|
436
|
-
var formatTimeline = (character, timeline) => `# ${character.name}'s Home Timeline
|
|
437
|
-
${timeline.map(formatCast).join("\n")}
|
|
438
|
-
`;
|
|
439
|
-
|
|
440
|
-
// src/managers/interactions.ts
|
|
441
|
-
import { shouldRespondTemplate } from "@elizaos/core";
|
|
607
|
+
// src/managers/post.ts
|
|
608
|
+
init_constants();
|
|
442
609
|
|
|
443
610
|
// src/common/types.ts
|
|
611
|
+
init_constants();
|
|
444
612
|
import { z } from "zod";
|
|
445
613
|
var FarcasterConfigSchema = z.object({
|
|
446
614
|
FARCASTER_DRY_RUN: z.union([z.boolean(), z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
447
615
|
FARCASTER_FID: z.number().int().min(1, "Farcaster fid is required"),
|
|
448
616
|
MAX_CAST_LENGTH: z.number().int().default(DEFAULT_MAX_CAST_LENGTH),
|
|
449
617
|
FARCASTER_POLL_INTERVAL: z.number().int().default(DEFAULT_POLL_INTERVAL),
|
|
618
|
+
FARCASTER_MODE: z.enum(["polling", "webhook"]).default("polling"),
|
|
450
619
|
ENABLE_CAST: z.union([z.boolean(), z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
451
620
|
CAST_INTERVAL_MIN: z.number().int(),
|
|
452
621
|
CAST_INTERVAL_MAX: z.number().int(),
|
|
@@ -459,52 +628,356 @@ var FarcasterConfigSchema = z.object({
|
|
|
459
628
|
FARCASTER_HUB_URL: z.string().min(1, "FARCASTER_HUB_URL is not set")
|
|
460
629
|
});
|
|
461
630
|
|
|
462
|
-
// src/managers/
|
|
463
|
-
|
|
464
|
-
timeout;
|
|
465
|
-
isRunning = false;
|
|
631
|
+
// src/managers/post.ts
|
|
632
|
+
class FarcasterCastManager {
|
|
466
633
|
client;
|
|
467
634
|
runtime;
|
|
635
|
+
fid;
|
|
636
|
+
timeout;
|
|
468
637
|
config;
|
|
469
|
-
|
|
638
|
+
isRunning = false;
|
|
470
639
|
constructor(opts) {
|
|
471
640
|
this.client = opts.client;
|
|
472
641
|
this.runtime = opts.runtime;
|
|
473
642
|
this.config = opts.config;
|
|
474
|
-
this.
|
|
643
|
+
this.fid = this.config.FARCASTER_FID;
|
|
644
|
+
}
|
|
645
|
+
async start() {
|
|
646
|
+
if (this.isRunning || !this.config.ENABLE_CAST) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
this.isRunning = true;
|
|
650
|
+
this.runPeriodically();
|
|
651
|
+
}
|
|
652
|
+
async stop() {
|
|
653
|
+
if (this.timeout)
|
|
654
|
+
clearTimeout(this.timeout);
|
|
655
|
+
this.isRunning = false;
|
|
656
|
+
}
|
|
657
|
+
calculateDelay() {
|
|
658
|
+
const minMinutes = this.config.CAST_INTERVAL_MIN;
|
|
659
|
+
const maxMinutes = this.config.CAST_INTERVAL_MAX;
|
|
660
|
+
const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes;
|
|
661
|
+
const delay = randomMinutes * 60 * 1000;
|
|
662
|
+
return { delay, randomMinutes };
|
|
663
|
+
}
|
|
664
|
+
async runPeriodically() {
|
|
665
|
+
if (this.config.CAST_IMMEDIATELY) {
|
|
666
|
+
await this.generateNewCast();
|
|
667
|
+
}
|
|
668
|
+
while (this.isRunning) {
|
|
669
|
+
try {
|
|
670
|
+
const lastPost = await this.runtime.getCache(lastCastCacheKey(this.fid));
|
|
671
|
+
const lastPostTimestamp = lastPost?.timestamp ?? 0;
|
|
672
|
+
const { delay, randomMinutes } = this.calculateDelay();
|
|
673
|
+
if (Date.now() > lastPostTimestamp + delay) {
|
|
674
|
+
await this.generateNewCast();
|
|
675
|
+
}
|
|
676
|
+
this.runtime.logger.log(`Next cast scheduled in ${randomMinutes} minutes`);
|
|
677
|
+
await new Promise((resolve) => this.timeout = setTimeout(resolve, delay));
|
|
678
|
+
} catch (error) {
|
|
679
|
+
this.runtime.logger.error({ agentId: this.runtime.agentId, error }, "[Farcaster] Error in periodic cast loop:");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async generateNewCast() {
|
|
684
|
+
this.runtime.logger.info("Generating new cast");
|
|
685
|
+
try {
|
|
686
|
+
const worldId = createUniqueUuid(this.runtime, this.fid.toString());
|
|
687
|
+
const roomId = createUniqueUuid(this.runtime, `${this.fid}-home`);
|
|
688
|
+
const callback = standardCastHandlerCallback({
|
|
689
|
+
client: this.client,
|
|
690
|
+
runtime: this.runtime,
|
|
691
|
+
config: this.config,
|
|
692
|
+
roomId,
|
|
693
|
+
onCompletion: async (casts, _memories) => {
|
|
694
|
+
const lastCast = casts[casts.length - 1];
|
|
695
|
+
await this.runtime.setCache(lastCastCacheKey(this.fid), {
|
|
696
|
+
hash: lastCast.hash,
|
|
697
|
+
timestamp: new Date(lastCast.timestamp).getTime()
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
this.runtime.emitEvent([EventType.POST_GENERATED, "FARCASTER_CAST_GENERATED" /* CAST_GENERATED */], {
|
|
702
|
+
runtime: this.runtime,
|
|
703
|
+
callback,
|
|
704
|
+
worldId,
|
|
705
|
+
userId: this.runtime.agentId,
|
|
706
|
+
roomId,
|
|
707
|
+
source: FARCASTER_SOURCE
|
|
708
|
+
});
|
|
709
|
+
} catch (error) {
|
|
710
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error generating new cast");
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// src/managers/interactions.ts
|
|
716
|
+
import {
|
|
717
|
+
ChannelType,
|
|
718
|
+
createUniqueUuid as createUniqueUuid2,
|
|
719
|
+
logger as logger3,
|
|
720
|
+
stringToUuid as stringToUuid2
|
|
721
|
+
} from "@elizaos/core";
|
|
722
|
+
|
|
723
|
+
// src/common/asyncqueue.ts
|
|
724
|
+
class AsyncQueue {
|
|
725
|
+
queue = [];
|
|
726
|
+
running = 0;
|
|
727
|
+
emptyListeners = [];
|
|
728
|
+
maxConcurrent;
|
|
729
|
+
constructor(maxConcurrent = 1) {
|
|
730
|
+
this.maxConcurrent = maxConcurrent;
|
|
731
|
+
}
|
|
732
|
+
async submit(work) {
|
|
733
|
+
return new Promise((resolve, reject) => {
|
|
734
|
+
this.queue.push(async () => {
|
|
735
|
+
try {
|
|
736
|
+
resolve(await work());
|
|
737
|
+
} catch (err) {
|
|
738
|
+
reject(err);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
this.doNextWork();
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
async doNextWork() {
|
|
745
|
+
if (this.running >= this.maxConcurrent) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const work = this.queue.shift();
|
|
749
|
+
if (!work) {
|
|
750
|
+
this.checkIfEmptyAndNotify();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
this.running++;
|
|
754
|
+
try {
|
|
755
|
+
await work();
|
|
756
|
+
} catch (err) {} finally {
|
|
757
|
+
this.running--;
|
|
758
|
+
this.doNextWork();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async size() {
|
|
762
|
+
return this.queue.length;
|
|
475
763
|
}
|
|
764
|
+
async waitUntilFinished() {
|
|
765
|
+
return new Promise((resolve) => {
|
|
766
|
+
if (this.queue.length === 0 && this.running === 0) {
|
|
767
|
+
resolve();
|
|
768
|
+
} else {
|
|
769
|
+
this.emptyListeners.push(resolve);
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
checkIfEmptyAndNotify() {
|
|
774
|
+
if (this.queue.length === 0 && this.running === 0) {
|
|
775
|
+
while (this.emptyListeners.length) {
|
|
776
|
+
const listener = this.emptyListeners.shift();
|
|
777
|
+
if (listener)
|
|
778
|
+
listener();
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/managers/interactions.ts
|
|
785
|
+
init_constants();
|
|
786
|
+
|
|
787
|
+
// src/managers/interaction-source.ts
|
|
788
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
789
|
+
class FarcasterInteractionSource {
|
|
790
|
+
client;
|
|
791
|
+
runtime;
|
|
792
|
+
config;
|
|
793
|
+
processor;
|
|
794
|
+
isRunning = false;
|
|
795
|
+
constructor(params) {
|
|
796
|
+
this.client = params.client;
|
|
797
|
+
this.runtime = params.runtime;
|
|
798
|
+
this.config = params.config;
|
|
799
|
+
this.processor = params.processor;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
class FarcasterPollingSource extends FarcasterInteractionSource {
|
|
804
|
+
timeout;
|
|
476
805
|
async start() {
|
|
477
|
-
logger2.info("Starting Farcaster
|
|
806
|
+
logger2.info("Starting Farcaster polling mode");
|
|
478
807
|
if (this.isRunning) {
|
|
479
808
|
return;
|
|
480
809
|
}
|
|
481
810
|
this.isRunning = true;
|
|
482
|
-
|
|
811
|
+
this.runPeriodically();
|
|
483
812
|
}
|
|
484
813
|
async stop() {
|
|
485
|
-
|
|
814
|
+
logger2.info("Stopping Farcaster polling mode");
|
|
815
|
+
if (this.timeout)
|
|
816
|
+
clearTimeout(this.timeout);
|
|
486
817
|
this.isRunning = false;
|
|
487
818
|
}
|
|
488
819
|
async runPeriodically() {
|
|
489
820
|
while (this.isRunning) {
|
|
490
821
|
try {
|
|
491
|
-
await this.
|
|
492
|
-
const delay = this.config.FARCASTER_POLL_INTERVAL *
|
|
493
|
-
await new Promise((resolve) => this.timeout = setTimeout(resolve, delay));
|
|
822
|
+
await this.pollForInteractions();
|
|
823
|
+
const delay = this.config.FARCASTER_POLL_INTERVAL * 1000;
|
|
824
|
+
await new Promise((resolve) => this.timeout = setTimeout(resolve, delay));
|
|
825
|
+
} catch (error) {
|
|
826
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error in polling:");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
async pollForInteractions() {
|
|
831
|
+
const agentFid = this.config.FARCASTER_FID;
|
|
832
|
+
const mentions = await this.client.getMentions({
|
|
833
|
+
fid: agentFid,
|
|
834
|
+
pageSize: 20
|
|
835
|
+
});
|
|
836
|
+
for (const cast of mentions) {
|
|
837
|
+
try {
|
|
838
|
+
const mention = neynarCastToCast(cast);
|
|
839
|
+
const memoryId = castUuid({ agentId: this.runtime.agentId, hash: mention.hash });
|
|
840
|
+
if (await this.runtime.getMemoryById(memoryId)) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
this.runtime.logger.info({ hash: mention.hash }, "New Cast found");
|
|
844
|
+
if (mention.authorFid === agentFid) {
|
|
845
|
+
const memory = await this.processor.ensureCastConnection(mention);
|
|
846
|
+
await this.runtime.addEmbeddingToMemory(memory);
|
|
847
|
+
await this.runtime.createMemory(memory, "messages");
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
await this.processor.processMention(cast);
|
|
851
|
+
} catch (error) {
|
|
852
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error processing mention:");
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
class FarcasterWebhookSource extends FarcasterInteractionSource {
|
|
859
|
+
async start() {
|
|
860
|
+
logger2.info("Starting Farcaster webhook mode");
|
|
861
|
+
if (this.isRunning) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
this.isRunning = true;
|
|
865
|
+
logger2.info("Webhook source is active - waiting for webhook events");
|
|
866
|
+
}
|
|
867
|
+
async stop() {
|
|
868
|
+
logger2.info("Stopping Farcaster webhook mode");
|
|
869
|
+
this.isRunning = false;
|
|
870
|
+
}
|
|
871
|
+
async processWebhookData(webhookData) {
|
|
872
|
+
if (!this.isRunning) {
|
|
873
|
+
logger2.warn("Webhook source is not running, ignoring webhook data");
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
try {
|
|
877
|
+
await this.processor.processWebhookData(webhookData);
|
|
878
|
+
} catch (error) {
|
|
879
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error processing webhook data:");
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function createFarcasterInteractionSource(params) {
|
|
884
|
+
const mode = params.config.FARCASTER_MODE;
|
|
885
|
+
switch (mode) {
|
|
886
|
+
case "webhook":
|
|
887
|
+
return new FarcasterWebhookSource(params);
|
|
888
|
+
case "polling":
|
|
889
|
+
default:
|
|
890
|
+
return new FarcasterPollingSource(params);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// src/managers/interactions.ts
|
|
895
|
+
class FarcasterInteractionManager {
|
|
896
|
+
client;
|
|
897
|
+
runtime;
|
|
898
|
+
config;
|
|
899
|
+
asyncQueue;
|
|
900
|
+
mode;
|
|
901
|
+
source;
|
|
902
|
+
constructor(opts) {
|
|
903
|
+
this.client = opts.client;
|
|
904
|
+
this.runtime = opts.runtime;
|
|
905
|
+
this.config = opts.config;
|
|
906
|
+
this.asyncQueue = new AsyncQueue(1);
|
|
907
|
+
this.mode = opts.config.FARCASTER_MODE;
|
|
908
|
+
this.source = createFarcasterInteractionSource({
|
|
909
|
+
client: this.client,
|
|
910
|
+
runtime: this.runtime,
|
|
911
|
+
config: this.config,
|
|
912
|
+
processor: this
|
|
913
|
+
});
|
|
914
|
+
logger3.info(`Farcaster interaction mode: ${this.mode}`);
|
|
915
|
+
}
|
|
916
|
+
async processMention(cast) {
|
|
917
|
+
const agentFid = this.config.FARCASTER_FID;
|
|
918
|
+
const agent = await this.client.getProfile(agentFid);
|
|
919
|
+
const mention = neynarCastToCast(cast);
|
|
920
|
+
await this.handleMentionCast({ agent, mention, cast });
|
|
921
|
+
}
|
|
922
|
+
async processReply(cast) {
|
|
923
|
+
const agentFid = this.config.FARCASTER_FID;
|
|
924
|
+
const agent = await this.client.getProfile(agentFid);
|
|
925
|
+
const reply = neynarCastToCast(cast);
|
|
926
|
+
await this.handleMentionCast({ agent, mention: reply, cast });
|
|
927
|
+
}
|
|
928
|
+
async processWebhookData(webhookData) {
|
|
929
|
+
if (webhookData.type !== "cast.created" || !webhookData.data) {
|
|
930
|
+
logger3.debug("Ignoring non-cast webhook event:", webhookData.type);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const castData = webhookData.data;
|
|
934
|
+
const agentFid = this.config.FARCASTER_FID;
|
|
935
|
+
if (!castData.author || !castData.hash || typeof castData.author.fid !== "number") {
|
|
936
|
+
logger3.warn("Invalid webhook cast data structure - missing author, hash, or author.fid");
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (castData.author.fid === agentFid) {
|
|
940
|
+
logger3.debug("Skipping webhook event from agent itself");
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const memoryId = castUuid({ agentId: this.runtime.agentId, hash: castData.hash });
|
|
944
|
+
if (await this.runtime.getMemoryById(memoryId)) {
|
|
945
|
+
logger3.debug("Skipping already processed webhook cast:", castData.hash);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const isMention = castData.mentioned_profiles?.some((profile) => profile.fid === agentFid);
|
|
949
|
+
const isReply = castData.parent_hash && castData.parent_author?.fid === agentFid;
|
|
950
|
+
if (isMention) {
|
|
951
|
+
const username = castData.author.username || "unknown";
|
|
952
|
+
const text = castData.text || "";
|
|
953
|
+
logger3.info(`Processing webhook MENTION from @${username}: "${text}"`);
|
|
954
|
+
try {
|
|
955
|
+
const neynarCast = await this.client.getCast(castData.hash);
|
|
956
|
+
await this.processMention(neynarCast);
|
|
957
|
+
} catch (error) {
|
|
958
|
+
logger3.error({ agentId: this.runtime.agentId, error }, "Failed to process webhook mention from @" + username);
|
|
959
|
+
}
|
|
960
|
+
} else if (isReply) {
|
|
961
|
+
const username = castData.author.username || "unknown";
|
|
962
|
+
const text = castData.text || "";
|
|
963
|
+
logger3.info(`Processing webhook REPLY from @${username}: "${text}"`);
|
|
964
|
+
try {
|
|
965
|
+
const neynarCast = await this.client.getCast(castData.hash);
|
|
966
|
+
await this.processReply(neynarCast);
|
|
494
967
|
} catch (error) {
|
|
495
|
-
|
|
968
|
+
this.runtime.logger.error({ error }, `Failed to process webhook reply from @${username}:`);
|
|
496
969
|
}
|
|
970
|
+
} else {
|
|
971
|
+
logger3.debug("Webhook cast is neither mention nor reply to agent");
|
|
497
972
|
}
|
|
498
973
|
}
|
|
499
974
|
async ensureCastConnection(cast) {
|
|
500
975
|
return await this.asyncQueue.submit(async () => {
|
|
501
|
-
var _a, _b;
|
|
502
976
|
const memoryId = castUuid({ agentId: this.runtime.agentId, hash: cast.hash });
|
|
503
|
-
const conversationId = cast.threadId ??
|
|
504
|
-
const entityId =
|
|
505
|
-
const worldId =
|
|
506
|
-
const
|
|
507
|
-
const roomId = createUniqueUuid(this.runtime, conversationId);
|
|
977
|
+
const conversationId = cast.threadId ?? cast.inReplyTo?.hash ?? cast.hash;
|
|
978
|
+
const entityId = createUniqueUuid2(this.runtime, cast.authorFid.toString());
|
|
979
|
+
const worldId = createUniqueUuid2(this.runtime, cast.authorFid.toString());
|
|
980
|
+
const roomId = createUniqueUuid2(this.runtime, conversationId);
|
|
508
981
|
if (entityId !== this.runtime.agentId) {
|
|
509
982
|
await this.runtime.ensureConnection({
|
|
510
983
|
entityId,
|
|
@@ -515,7 +988,7 @@ var FarcasterInteractionManager = class {
|
|
|
515
988
|
source: FARCASTER_SOURCE,
|
|
516
989
|
type: ChannelType.THREAD,
|
|
517
990
|
channelId: conversationId,
|
|
518
|
-
|
|
991
|
+
messageServerId: stringToUuid2(cast.authorFid.toString()),
|
|
519
992
|
worldId,
|
|
520
993
|
metadata: {
|
|
521
994
|
ownership: { ownerId: cast.authorFid.toString() },
|
|
@@ -532,8 +1005,7 @@ var FarcasterInteractionManager = class {
|
|
|
532
1005
|
agentId: this.runtime.agentId,
|
|
533
1006
|
content: {
|
|
534
1007
|
text: cast.text,
|
|
535
|
-
|
|
536
|
-
inReplyTo: ((_b = cast.inReplyTo) == null ? void 0 : _b.hash) ? castUuid({ agentId: this.runtime.agentId, hash: cast.inReplyTo.hash }) : void 0,
|
|
1008
|
+
inReplyTo: cast.inReplyTo?.hash ? castUuid({ agentId: this.runtime.agentId, hash: cast.inReplyTo.hash }) : undefined,
|
|
537
1009
|
source: FARCASTER_SOURCE,
|
|
538
1010
|
channelType: ChannelType.THREAD
|
|
539
1011
|
},
|
|
@@ -544,34 +1016,9 @@ var FarcasterInteractionManager = class {
|
|
|
544
1016
|
return memory;
|
|
545
1017
|
});
|
|
546
1018
|
}
|
|
547
|
-
async handleInteractions() {
|
|
548
|
-
const agentFid = this.config.FARCASTER_FID;
|
|
549
|
-
const [mentions, agent] = await Promise.all([
|
|
550
|
-
this.client.getMentions({
|
|
551
|
-
fid: agentFid,
|
|
552
|
-
pageSize: 20
|
|
553
|
-
}),
|
|
554
|
-
this.client.getProfile(agentFid)
|
|
555
|
-
]);
|
|
556
|
-
for (const cast of mentions) {
|
|
557
|
-
const mention = neynarCastToCast(cast);
|
|
558
|
-
const memoryId = castUuid({ agentId: this.runtime.agentId, hash: mention.hash });
|
|
559
|
-
if (await this.runtime.getMemoryById(memoryId)) {
|
|
560
|
-
continue;
|
|
561
|
-
}
|
|
562
|
-
logger2.info("New Cast found", mention.hash);
|
|
563
|
-
if (mention.authorFid === agentFid) {
|
|
564
|
-
const memory = await this.ensureCastConnection(mention);
|
|
565
|
-
await this.runtime.addEmbeddingToMemory(memory);
|
|
566
|
-
await this.runtime.createMemory(memory, "messages");
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
await this.handleMentionCast({ agent, mention, cast });
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
1019
|
async buildThreadForCast(cast, skipMemoryId) {
|
|
573
1020
|
const thread = [];
|
|
574
|
-
const visited =
|
|
1021
|
+
const visited = new Set;
|
|
575
1022
|
const client = this.client;
|
|
576
1023
|
const runtime = this.runtime;
|
|
577
1024
|
const self = this;
|
|
@@ -583,12 +1030,12 @@ var FarcasterInteractionManager = class {
|
|
|
583
1030
|
visited.add(currentCast.hash);
|
|
584
1031
|
const memory = await runtime.getMemoryById(memoryId);
|
|
585
1032
|
if (!memory) {
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
await runtime.createMemory(
|
|
1033
|
+
logger3.info({ hash: currentCast.hash }, "Creating memory for cast");
|
|
1034
|
+
const newMemory = await self.ensureCastConnection(currentCast);
|
|
1035
|
+
await runtime.createMemory(newMemory, "messages");
|
|
589
1036
|
runtime.emitEvent("FARCASTER_THREAD_CAST_CREATED" /* THREAD_CAST_CREATED */, {
|
|
590
1037
|
runtime,
|
|
591
|
-
memory:
|
|
1038
|
+
memory: newMemory,
|
|
592
1039
|
cast: currentCast,
|
|
593
1040
|
source: FARCASTER_SOURCE
|
|
594
1041
|
});
|
|
@@ -607,51 +1054,14 @@ var FarcasterInteractionManager = class {
|
|
|
607
1054
|
mention,
|
|
608
1055
|
cast
|
|
609
1056
|
}) {
|
|
610
|
-
var _a, _b, _c;
|
|
611
1057
|
if (mention.profile.fid === agent.fid) {
|
|
612
|
-
|
|
1058
|
+
logger3.info({ hash: mention.hash }, "skipping cast from bot itself");
|
|
613
1059
|
return;
|
|
614
1060
|
}
|
|
615
1061
|
const memory = await this.ensureCastConnection(mention);
|
|
616
|
-
const thread = await this.buildThreadForCast(
|
|
617
|
-
mention,
|
|
618
|
-
memory.id ? /* @__PURE__ */ new Set([memory.id]) : /* @__PURE__ */ new Set()
|
|
619
|
-
);
|
|
1062
|
+
const thread = await this.buildThreadForCast(mention, memory.id ? new Set([memory.id]) : new Set);
|
|
620
1063
|
if (!memory.content.text || memory.content.text.trim() === "") {
|
|
621
|
-
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
const currentPost = formatCast(mention);
|
|
625
|
-
const { timeline } = await this.client.getTimeline({ fid: agent.fid, pageSize: 20 });
|
|
626
|
-
const formattedTimeline = formatTimeline(this.runtime.character, timeline);
|
|
627
|
-
const formattedConversation = thread.map(
|
|
628
|
-
(c) => `
|
|
629
|
-
- @${c.profile.username} (${formatCastTimestamp(c.timestamp)}):
|
|
630
|
-
${c.text}`.trim()
|
|
631
|
-
).join("\n\n");
|
|
632
|
-
const state = await this.runtime.composeState(memory);
|
|
633
|
-
state.values = {
|
|
634
|
-
...state.values,
|
|
635
|
-
farcasterUsername: agent.username,
|
|
636
|
-
timeline: formattedTimeline,
|
|
637
|
-
currentPost,
|
|
638
|
-
formattedConversation
|
|
639
|
-
};
|
|
640
|
-
const shouldRespondPrompt = composePrompt({
|
|
641
|
-
state,
|
|
642
|
-
template: ((_a = this.runtime.character.templates) == null ? void 0 : _a.farcasterShouldRespondTemplate) || ((_c = (_b = this.runtime.character) == null ? void 0 : _b.templates) == null ? void 0 : _c.shouldRespondTemplate) || shouldRespondTemplate
|
|
643
|
-
});
|
|
644
|
-
const response = await this.runtime.useModel(ModelType.TEXT_SMALL, {
|
|
645
|
-
prompt: shouldRespondPrompt
|
|
646
|
-
});
|
|
647
|
-
const responseActions = (response.match(/(?:RESPOND|IGNORE|STOP)/g) || ["IGNORE"])[0];
|
|
648
|
-
if (responseActions !== "RESPOND") {
|
|
649
|
-
logger2.info(`Not responding to cast based on shouldRespond decision: ${responseActions}`);
|
|
650
|
-
try {
|
|
651
|
-
await this.runtime.createMemory(memory, "messages");
|
|
652
|
-
} catch (error) {
|
|
653
|
-
logger2.error("Error creating ignoredmemory", error);
|
|
654
|
-
}
|
|
1064
|
+
logger3.info({ hash: mention.hash }, "skipping cast with no text");
|
|
655
1065
|
return;
|
|
656
1066
|
}
|
|
657
1067
|
const callback = standardCastHandlerCallback({
|
|
@@ -664,113 +1074,39 @@ var FarcasterInteractionManager = class {
|
|
|
664
1074
|
fid: mention.authorFid
|
|
665
1075
|
}
|
|
666
1076
|
});
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
1077
|
+
try {
|
|
1078
|
+
if (!this.runtime.messageService) {
|
|
1079
|
+
logger3.warn("[Farcaster] messageService not available, skipping mention handling");
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
await this.runtime.messageService.handleMessage(this.runtime, memory, callback);
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
logger3.error({
|
|
1085
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1086
|
+
castHash: mention.hash
|
|
1087
|
+
}, "[Farcaster] Error processing mention");
|
|
1088
|
+
}
|
|
674
1089
|
const mentionPayload = {
|
|
675
1090
|
runtime: this.runtime,
|
|
676
1091
|
memory,
|
|
677
1092
|
cast,
|
|
678
1093
|
source: FARCASTER_SOURCE,
|
|
679
|
-
callback
|
|
680
|
-
logger2.info("[Farcaster] mention received response:", response);
|
|
681
|
-
return [];
|
|
682
|
-
}
|
|
1094
|
+
callback
|
|
683
1095
|
};
|
|
684
1096
|
this.runtime.emitEvent("FARCASTER_MENTION_RECEIVED" /* MENTION_RECEIVED */, mentionPayload);
|
|
685
1097
|
}
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
// src/managers/post.ts
|
|
689
|
-
import { createUniqueUuid as createUniqueUuid2, EventType as EventType2, logger as logger3 } from "@elizaos/core";
|
|
690
|
-
var FarcasterCastManager = class {
|
|
691
|
-
client;
|
|
692
|
-
runtime;
|
|
693
|
-
fid;
|
|
694
|
-
timeout;
|
|
695
|
-
config;
|
|
696
|
-
isRunning = false;
|
|
697
|
-
constructor(opts) {
|
|
698
|
-
this.client = opts.client;
|
|
699
|
-
this.runtime = opts.runtime;
|
|
700
|
-
this.config = opts.config;
|
|
701
|
-
this.fid = this.config.FARCASTER_FID;
|
|
702
|
-
}
|
|
703
1098
|
async start() {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
this.isRunning = true;
|
|
708
|
-
void this.runPeriodically();
|
|
1099
|
+
logger3.info(`Starting Farcaster interaction manager in ${this.mode} mode`);
|
|
1100
|
+
await this.source.start();
|
|
709
1101
|
}
|
|
710
1102
|
async stop() {
|
|
711
|
-
|
|
712
|
-
this.
|
|
713
|
-
}
|
|
714
|
-
calculateDelay() {
|
|
715
|
-
const minMinutes = this.config.CAST_INTERVAL_MIN;
|
|
716
|
-
const maxMinutes = this.config.CAST_INTERVAL_MAX;
|
|
717
|
-
const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes;
|
|
718
|
-
const delay = randomMinutes * 60 * 1e3;
|
|
719
|
-
return { delay, randomMinutes };
|
|
720
|
-
}
|
|
721
|
-
async runPeriodically() {
|
|
722
|
-
if (this.config.CAST_IMMEDIATELY) {
|
|
723
|
-
await this.generateNewCast();
|
|
724
|
-
}
|
|
725
|
-
while (this.isRunning) {
|
|
726
|
-
try {
|
|
727
|
-
const lastPost = await this.runtime.getCache(lastCastCacheKey(this.fid));
|
|
728
|
-
const lastPostTimestamp = (lastPost == null ? void 0 : lastPost.timestamp) ?? 0;
|
|
729
|
-
const { delay, randomMinutes } = this.calculateDelay();
|
|
730
|
-
if (Date.now() > lastPostTimestamp + delay) {
|
|
731
|
-
await this.generateNewCast();
|
|
732
|
-
}
|
|
733
|
-
logger3.log(`Next cast scheduled in ${randomMinutes} minutes`);
|
|
734
|
-
await new Promise((resolve) => this.timeout = setTimeout(resolve, delay));
|
|
735
|
-
} catch (error) {
|
|
736
|
-
logger3.error("[Farcaster] Error in periodic cast loop:", this.runtime.agentId, error);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
async generateNewCast() {
|
|
741
|
-
logger3.info("Generating new cast");
|
|
742
|
-
try {
|
|
743
|
-
const worldId = createUniqueUuid2(this.runtime, this.fid.toString());
|
|
744
|
-
const roomId = createUniqueUuid2(this.runtime, `${this.fid}-home`);
|
|
745
|
-
const callback = standardCastHandlerCallback({
|
|
746
|
-
client: this.client,
|
|
747
|
-
runtime: this.runtime,
|
|
748
|
-
config: this.config,
|
|
749
|
-
roomId,
|
|
750
|
-
onCompletion: async (casts, _memories) => {
|
|
751
|
-
const lastCast = casts[casts.length - 1];
|
|
752
|
-
await this.runtime.setCache(lastCastCacheKey(this.fid), {
|
|
753
|
-
hash: lastCast.hash,
|
|
754
|
-
timestamp: new Date(lastCast.timestamp).getTime()
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
this.runtime.emitEvent([EventType2.POST_GENERATED, "FARCASTER_CAST_GENERATED" /* CAST_GENERATED */], {
|
|
759
|
-
runtime: this.runtime,
|
|
760
|
-
callback,
|
|
761
|
-
worldId,
|
|
762
|
-
userId: this.runtime.agentId,
|
|
763
|
-
roomId,
|
|
764
|
-
source: FARCASTER_SOURCE
|
|
765
|
-
});
|
|
766
|
-
} catch (error) {
|
|
767
|
-
logger3.error("Error generating new cast:", error);
|
|
768
|
-
}
|
|
1103
|
+
logger3.info("Stopping Farcaster interaction manager");
|
|
1104
|
+
await this.source.stop();
|
|
769
1105
|
}
|
|
770
|
-
}
|
|
1106
|
+
}
|
|
771
1107
|
|
|
772
1108
|
// src/managers/agent.ts
|
|
773
|
-
|
|
1109
|
+
class FarcasterAgentManager {
|
|
774
1110
|
runtime;
|
|
775
1111
|
client;
|
|
776
1112
|
casts;
|
|
@@ -783,8 +1119,8 @@ var FarcasterAgentManager = class {
|
|
|
783
1119
|
const client = new FarcasterClient({ neynar, signerUuid });
|
|
784
1120
|
this.client = client;
|
|
785
1121
|
logger4.success("Farcaster Neynar client initialized.");
|
|
786
|
-
this.casts = new FarcasterCastManager({ client, runtime, config });
|
|
787
1122
|
this.interactions = new FarcasterInteractionManager({ client, runtime, config });
|
|
1123
|
+
this.casts = new FarcasterCastManager({ client, runtime, config });
|
|
788
1124
|
}
|
|
789
1125
|
async start() {
|
|
790
1126
|
await Promise.all([this.casts.start(), this.interactions.start()]);
|
|
@@ -792,13 +1128,15 @@ var FarcasterAgentManager = class {
|
|
|
792
1128
|
async stop() {
|
|
793
1129
|
await Promise.all([this.casts.stop(), this.interactions.stop()]);
|
|
794
1130
|
}
|
|
795
|
-
}
|
|
1131
|
+
}
|
|
796
1132
|
|
|
797
1133
|
// src/common/config.ts
|
|
1134
|
+
init_constants();
|
|
798
1135
|
import { logger as logger5, parseBooleanFromText } from "@elizaos/core";
|
|
799
1136
|
import { ZodError } from "zod";
|
|
800
1137
|
function safeParseInt(value, defaultValue) {
|
|
801
|
-
if (!value)
|
|
1138
|
+
if (!value)
|
|
1139
|
+
return defaultValue;
|
|
802
1140
|
const parsed = Number.parseInt(value, 10);
|
|
803
1141
|
return Number.isNaN(parsed) ? defaultValue : Math.max(1, parsed);
|
|
804
1142
|
}
|
|
@@ -813,38 +1151,20 @@ function validateFarcasterConfig(runtime) {
|
|
|
813
1151
|
try {
|
|
814
1152
|
const farcasterConfig = {
|
|
815
1153
|
FARCASTER_DRY_RUN: runtime.getSetting("FARCASTER_DRY_RUN") || parseBooleanFromText(process.env.FARCASTER_DRY_RUN || "false"),
|
|
816
|
-
FARCASTER_FID: Number.isNaN(fid) ?
|
|
817
|
-
MAX_CAST_LENGTH: safeParseInt(
|
|
818
|
-
|
|
819
|
-
DEFAULT_MAX_CAST_LENGTH
|
|
820
|
-
),
|
|
821
|
-
FARCASTER_POLL_INTERVAL: safeParseInt(
|
|
822
|
-
runtime.getSetting("FARCASTER_POLL_INTERVAL") || process.env.FARCASTER_POLL_INTERVAL,
|
|
823
|
-
DEFAULT_POLL_INTERVAL
|
|
824
|
-
),
|
|
1154
|
+
FARCASTER_FID: Number.isNaN(fid) ? undefined : fid,
|
|
1155
|
+
MAX_CAST_LENGTH: safeParseInt(runtime.getSetting("MAX_CAST_LENGTH") || process.env.MAX_CAST_LENGTH, DEFAULT_MAX_CAST_LENGTH),
|
|
1156
|
+
FARCASTER_POLL_INTERVAL: safeParseInt(runtime.getSetting("FARCASTER_POLL_INTERVAL") || process.env.FARCASTER_POLL_INTERVAL, DEFAULT_POLL_INTERVAL),
|
|
825
1157
|
ENABLE_CAST: runtime.getSetting("ENABLE_CAST") || parseBooleanFromText(process.env.ENABLE_CAST || "true"),
|
|
826
|
-
CAST_INTERVAL_MIN: safeParseInt(
|
|
827
|
-
|
|
828
|
-
DEFAULT_CAST_INTERVAL_MIN
|
|
829
|
-
),
|
|
830
|
-
CAST_INTERVAL_MAX: safeParseInt(
|
|
831
|
-
runtime.getSetting("CAST_INTERVAL_MAX") || process.env.CAST_INTERVAL_MAX,
|
|
832
|
-
DEFAULT_CAST_INTERVAL_MAX
|
|
833
|
-
),
|
|
1158
|
+
CAST_INTERVAL_MIN: safeParseInt(runtime.getSetting("CAST_INTERVAL_MIN") || process.env.CAST_INTERVAL_MIN, DEFAULT_CAST_INTERVAL_MIN),
|
|
1159
|
+
CAST_INTERVAL_MAX: safeParseInt(runtime.getSetting("CAST_INTERVAL_MAX") || process.env.CAST_INTERVAL_MAX, DEFAULT_CAST_INTERVAL_MAX),
|
|
834
1160
|
ENABLE_ACTION_PROCESSING: runtime.getSetting("ENABLE_ACTION_PROCESSING") || parseBooleanFromText(process.env.ENABLE_ACTION_PROCESSING || "false"),
|
|
835
|
-
ACTION_INTERVAL: safeParseInt(
|
|
836
|
-
runtime.getSetting("ACTION_INTERVAL") || process.env.ACTION_INTERVAL,
|
|
837
|
-
5
|
|
838
|
-
),
|
|
839
|
-
// 5 minutes
|
|
1161
|
+
ACTION_INTERVAL: safeParseInt(runtime.getSetting("ACTION_INTERVAL") || process.env.ACTION_INTERVAL, 5),
|
|
840
1162
|
CAST_IMMEDIATELY: runtime.getSetting("CAST_IMMEDIATELY") || parseBooleanFromText(process.env.CAST_IMMEDIATELY || "false"),
|
|
841
|
-
MAX_ACTIONS_PROCESSING: safeParseInt(
|
|
842
|
-
runtime.getSetting("MAX_ACTIONS_PROCESSING") || process.env.MAX_ACTIONS_PROCESSING,
|
|
843
|
-
1
|
|
844
|
-
),
|
|
1163
|
+
MAX_ACTIONS_PROCESSING: safeParseInt(runtime.getSetting("MAX_ACTIONS_PROCESSING") || process.env.MAX_ACTIONS_PROCESSING, 1),
|
|
845
1164
|
FARCASTER_SIGNER_UUID: runtime.getSetting("FARCASTER_SIGNER_UUID") || process.env.FARCASTER_SIGNER_UUID,
|
|
846
1165
|
FARCASTER_NEYNAR_API_KEY: runtime.getSetting("FARCASTER_NEYNAR_API_KEY") || process.env.FARCASTER_NEYNAR_API_KEY,
|
|
847
|
-
FARCASTER_HUB_URL: runtime.getSetting("FARCASTER_HUB_URL") || process.env.FARCASTER_HUB_URL || "hub.pinata.cloud"
|
|
1166
|
+
FARCASTER_HUB_URL: runtime.getSetting("FARCASTER_HUB_URL") || process.env.FARCASTER_HUB_URL || "hub.pinata.cloud",
|
|
1167
|
+
FARCASTER_MODE: runtime.getSetting("FARCASTER_MODE") || process.env.FARCASTER_MODE || "polling"
|
|
848
1168
|
};
|
|
849
1169
|
const config = FarcasterConfigSchema.parse(farcasterConfig);
|
|
850
1170
|
const isDryRun = config.FARCASTER_DRY_RUN;
|
|
@@ -853,9 +1173,7 @@ function validateFarcasterConfig(runtime) {
|
|
|
853
1173
|
logger5.log(`- Dry Run Mode: ${isDryRun ? "enabled" : "disabled"}`);
|
|
854
1174
|
logger5.log(`- Enable Cast: ${config.ENABLE_CAST ? "enabled" : "disabled"}`);
|
|
855
1175
|
if (config.ENABLE_CAST) {
|
|
856
|
-
logger5.log(
|
|
857
|
-
`- Cast Interval: ${config.CAST_INTERVAL_MIN}-${config.CAST_INTERVAL_MAX} minutes`
|
|
858
|
-
);
|
|
1176
|
+
logger5.log(`- Cast Interval: ${config.CAST_INTERVAL_MIN}-${config.CAST_INTERVAL_MAX} minutes`);
|
|
859
1177
|
logger5.log(`- Cast Immediately: ${config.CAST_IMMEDIATELY ? "enabled" : "disabled"}`);
|
|
860
1178
|
}
|
|
861
1179
|
logger5.log(`- Action Processing: ${config.ENABLE_ACTION_PROCESSING ? "enabled" : "disabled"}`);
|
|
@@ -866,7 +1184,8 @@ function validateFarcasterConfig(runtime) {
|
|
|
866
1184
|
return config;
|
|
867
1185
|
} catch (error) {
|
|
868
1186
|
if (error instanceof ZodError) {
|
|
869
|
-
const errorMessages = error.
|
|
1187
|
+
const errorMessages = error.issues.map((err) => `${err.path.join(".")}: ${err.message}`).join(`
|
|
1188
|
+
`);
|
|
870
1189
|
throw new Error(`Farcaster configuration validation failed:
|
|
871
1190
|
${errorMessages}`);
|
|
872
1191
|
}
|
|
@@ -875,64 +1194,74 @@ ${errorMessages}`);
|
|
|
875
1194
|
}
|
|
876
1195
|
|
|
877
1196
|
// src/services/MessageService.ts
|
|
878
|
-
import {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1197
|
+
import { createUniqueUuid as createUniqueUuid3 } from "@elizaos/core";
|
|
1198
|
+
init_constants();
|
|
1199
|
+
class FarcasterMessageService {
|
|
1200
|
+
client;
|
|
1201
|
+
runtime;
|
|
883
1202
|
constructor(client, runtime) {
|
|
884
1203
|
this.client = client;
|
|
885
1204
|
this.runtime = runtime;
|
|
886
1205
|
}
|
|
1206
|
+
castToMessage(cast, agentId, extraMetadata) {
|
|
1207
|
+
return {
|
|
1208
|
+
id: castUuid({ hash: cast.hash, agentId }),
|
|
1209
|
+
agentId,
|
|
1210
|
+
roomId: createUniqueUuid3(this.runtime, cast.threadId || cast.hash),
|
|
1211
|
+
userId: cast.profile.fid.toString(),
|
|
1212
|
+
username: cast.profile.username,
|
|
1213
|
+
text: cast.text,
|
|
1214
|
+
type: cast.inReplyTo ? "REPLY" /* REPLY */ : "CAST" /* CAST */,
|
|
1215
|
+
timestamp: cast.timestamp.getTime(),
|
|
1216
|
+
inReplyTo: cast.inReplyTo ? castUuid({ hash: cast.inReplyTo.hash, agentId }) : undefined,
|
|
1217
|
+
metadata: {
|
|
1218
|
+
source: FARCASTER_SOURCE,
|
|
1219
|
+
castHash: cast.hash,
|
|
1220
|
+
threadId: cast.threadId,
|
|
1221
|
+
authorFid: cast.authorFid,
|
|
1222
|
+
...extraMetadata || {}
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
887
1226
|
async getMessages(options) {
|
|
888
1227
|
try {
|
|
889
|
-
const { roomId, limit = 20 } = options;
|
|
1228
|
+
const { agentId, roomId, limit = 20 } = options;
|
|
1229
|
+
const fidStr = this.runtime.getSetting("FARCASTER_FID");
|
|
1230
|
+
const fid = fidStr ? parseInt(fidStr, 10) : NaN;
|
|
1231
|
+
if (!fid || isNaN(fid)) {
|
|
1232
|
+
this.runtime.logger.error("[Farcaster] FARCASTER_FID is not configured");
|
|
1233
|
+
return [];
|
|
1234
|
+
}
|
|
890
1235
|
const { timeline } = await this.client.getTimeline({
|
|
891
|
-
fid
|
|
892
|
-
this.runtime.getSetting("FARCASTER_FID") || this.runtime.config.FARCASTER_FID
|
|
893
|
-
),
|
|
1236
|
+
fid,
|
|
894
1237
|
pageSize: limit
|
|
895
1238
|
});
|
|
896
|
-
const messages = timeline.
|
|
1239
|
+
const messages = timeline.map((cast) => this.castToMessage(cast, agentId)).filter((message) => {
|
|
897
1240
|
if (roomId) {
|
|
898
|
-
|
|
899
|
-
return castRoomId === roomId;
|
|
1241
|
+
return message.roomId === roomId;
|
|
900
1242
|
}
|
|
901
1243
|
return true;
|
|
902
|
-
})
|
|
903
|
-
id: castUuid({ hash: cast.hash, agentId: this.runtime.agentId }),
|
|
904
|
-
agentId: this.runtime.agentId,
|
|
905
|
-
roomId: createUniqueUuid3(this.runtime, cast.threadId || cast.hash),
|
|
906
|
-
userId: cast.profile.fid.toString(),
|
|
907
|
-
username: cast.profile.username,
|
|
908
|
-
text: cast.text,
|
|
909
|
-
type: cast.inReplyTo ? "REPLY" /* REPLY */ : "CAST" /* CAST */,
|
|
910
|
-
timestamp: cast.timestamp.getTime(),
|
|
911
|
-
inReplyTo: cast.inReplyTo ? castUuid({ hash: cast.inReplyTo.hash, agentId: this.runtime.agentId }) : void 0,
|
|
912
|
-
metadata: {
|
|
913
|
-
castHash: cast.hash,
|
|
914
|
-
threadId: cast.threadId,
|
|
915
|
-
authorFid: cast.authorFid
|
|
916
|
-
}
|
|
917
|
-
}));
|
|
1244
|
+
});
|
|
918
1245
|
return messages;
|
|
919
1246
|
} catch (error) {
|
|
920
|
-
|
|
1247
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error fetching messages");
|
|
921
1248
|
return [];
|
|
922
1249
|
}
|
|
923
1250
|
}
|
|
924
1251
|
async sendMessage(options) {
|
|
925
|
-
var _a;
|
|
926
1252
|
try {
|
|
927
1253
|
const { text, type, roomId, replyToId, agentId } = options;
|
|
928
|
-
let inReplyTo =
|
|
1254
|
+
let inReplyTo = undefined;
|
|
929
1255
|
if (replyToId && type === "REPLY" /* REPLY */) {
|
|
930
|
-
const parentHash =
|
|
1256
|
+
const parentHash = options.metadata?.parentHash || replyToId;
|
|
1257
|
+
const fidStr = this.runtime.getSetting("FARCASTER_FID");
|
|
1258
|
+
const fid = fidStr ? parseInt(fidStr, 10) : NaN;
|
|
1259
|
+
if (!fid || isNaN(fid)) {
|
|
1260
|
+
throw new Error("FARCASTER_FID is not configured");
|
|
1261
|
+
}
|
|
931
1262
|
inReplyTo = {
|
|
932
1263
|
hash: parentHash,
|
|
933
|
-
fid
|
|
934
|
-
this.runtime.getSetting("FARCASTER_FID") || this.runtime.config.FARCASTER_FID
|
|
935
|
-
)
|
|
1264
|
+
fid
|
|
936
1265
|
};
|
|
937
1266
|
}
|
|
938
1267
|
const casts = await this.client.sendCast({
|
|
@@ -943,23 +1272,9 @@ var FarcasterMessageService = class {
|
|
|
943
1272
|
throw new Error("No cast was created");
|
|
944
1273
|
}
|
|
945
1274
|
const cast = neynarCastToCast(casts[0]);
|
|
946
|
-
const message =
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
roomId,
|
|
950
|
-
userId: cast.profile.fid.toString(),
|
|
951
|
-
username: cast.profile.username,
|
|
952
|
-
text: cast.text,
|
|
953
|
-
type,
|
|
954
|
-
timestamp: cast.timestamp.getTime(),
|
|
955
|
-
inReplyTo: inReplyTo ? castUuid({ hash: inReplyTo.hash, agentId }) : void 0,
|
|
956
|
-
metadata: {
|
|
957
|
-
...options.metadata,
|
|
958
|
-
castHash: cast.hash,
|
|
959
|
-
threadId: cast.threadId,
|
|
960
|
-
authorFid: cast.authorFid
|
|
961
|
-
}
|
|
962
|
-
};
|
|
1275
|
+
const message = this.castToMessage(cast, agentId, options.metadata);
|
|
1276
|
+
message.roomId = roomId;
|
|
1277
|
+
message.type = type;
|
|
963
1278
|
await this.runtime.emitEvent("FARCASTER_CAST_GENERATED" /* CAST_GENERATED */, {
|
|
964
1279
|
runtime: this.runtime,
|
|
965
1280
|
castHash: cast.hash,
|
|
@@ -968,78 +1283,84 @@ var FarcasterMessageService = class {
|
|
|
968
1283
|
});
|
|
969
1284
|
return message;
|
|
970
1285
|
} catch (error) {
|
|
971
|
-
|
|
1286
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error sending message");
|
|
972
1287
|
throw error;
|
|
973
1288
|
}
|
|
974
1289
|
}
|
|
975
1290
|
async deleteMessage(messageId, agentId) {
|
|
976
|
-
|
|
1291
|
+
this.runtime.logger.warn("[Farcaster] Cast deletion is not supported by the Farcaster API");
|
|
977
1292
|
}
|
|
978
1293
|
async getMessage(messageId, agentId) {
|
|
979
1294
|
try {
|
|
980
1295
|
const castHash = messageId;
|
|
981
1296
|
const cast = await this.client.getCast(castHash);
|
|
982
1297
|
const farcasterCast = neynarCastToCast(cast);
|
|
983
|
-
|
|
984
|
-
id: castUuid({ hash: farcasterCast.hash, agentId }),
|
|
985
|
-
agentId,
|
|
986
|
-
roomId: createUniqueUuid3(this.runtime, farcasterCast.threadId || farcasterCast.hash),
|
|
987
|
-
userId: farcasterCast.profile.fid.toString(),
|
|
988
|
-
username: farcasterCast.profile.username,
|
|
989
|
-
text: farcasterCast.text,
|
|
990
|
-
type: farcasterCast.inReplyTo ? "REPLY" /* REPLY */ : "CAST" /* CAST */,
|
|
991
|
-
timestamp: farcasterCast.timestamp.getTime(),
|
|
992
|
-
inReplyTo: farcasterCast.inReplyTo ? castUuid({ hash: farcasterCast.inReplyTo.hash, agentId }) : void 0,
|
|
993
|
-
metadata: {
|
|
994
|
-
castHash: farcasterCast.hash,
|
|
995
|
-
threadId: farcasterCast.threadId,
|
|
996
|
-
authorFid: farcasterCast.authorFid
|
|
997
|
-
}
|
|
998
|
-
};
|
|
999
|
-
return message;
|
|
1298
|
+
return this.castToMessage(farcasterCast, agentId);
|
|
1000
1299
|
} catch (error) {
|
|
1001
|
-
|
|
1300
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error fetching message");
|
|
1002
1301
|
return null;
|
|
1003
1302
|
}
|
|
1004
1303
|
}
|
|
1304
|
+
async getThread(params) {
|
|
1305
|
+
try {
|
|
1306
|
+
const thread = [];
|
|
1307
|
+
const visited = new Set;
|
|
1308
|
+
let currentHash = params.castHash;
|
|
1309
|
+
while (currentHash) {
|
|
1310
|
+
if (visited.has(currentHash)) {
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
visited.add(currentHash);
|
|
1314
|
+
const cast = neynarCastToCast(await this.client.getCast(currentHash));
|
|
1315
|
+
thread.unshift(this.castToMessage(cast, params.agentId));
|
|
1316
|
+
currentHash = cast.inReplyTo?.hash;
|
|
1317
|
+
}
|
|
1318
|
+
return thread;
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
this.runtime.logger.error({ error }, "[Farcaster] Error fetching thread");
|
|
1321
|
+
return [];
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1005
1324
|
async markAsRead(messageIds, agentId) {
|
|
1006
|
-
|
|
1325
|
+
this.runtime.logger.debug("[Farcaster] Mark as read is not applicable for Farcaster casts");
|
|
1007
1326
|
}
|
|
1008
|
-
}
|
|
1327
|
+
}
|
|
1009
1328
|
|
|
1010
1329
|
// src/services/CastService.ts
|
|
1011
1330
|
import {
|
|
1012
|
-
logger as
|
|
1331
|
+
logger as logger6,
|
|
1013
1332
|
ModelType as ModelType2,
|
|
1014
1333
|
createUniqueUuid as createUniqueUuid4
|
|
1015
1334
|
} from "@elizaos/core";
|
|
1016
|
-
|
|
1335
|
+
init_constants();
|
|
1336
|
+
|
|
1337
|
+
class FarcasterCastService {
|
|
1338
|
+
client;
|
|
1339
|
+
runtime;
|
|
1340
|
+
static serviceType = "ICastService";
|
|
1017
1341
|
constructor(client, runtime) {
|
|
1018
1342
|
this.client = client;
|
|
1019
1343
|
this.runtime = runtime;
|
|
1020
1344
|
}
|
|
1021
|
-
static serviceType = "ICastService";
|
|
1022
|
-
/**
|
|
1023
|
-
* Get recent casts from the timeline
|
|
1024
|
-
*/
|
|
1025
1345
|
async getCasts(params) {
|
|
1026
|
-
var _a, _b;
|
|
1027
1346
|
try {
|
|
1347
|
+
const fidStr = this.runtime.getSetting("FARCASTER_FID");
|
|
1348
|
+
const fid = fidStr ? parseInt(fidStr, 10) : NaN;
|
|
1349
|
+
if (!fid || isNaN(fid)) {
|
|
1350
|
+
logger6.error("FARCASTER_FID is not configured");
|
|
1351
|
+
return [];
|
|
1352
|
+
}
|
|
1028
1353
|
const { timeline } = await this.client.getTimeline({
|
|
1029
|
-
fid
|
|
1354
|
+
fid,
|
|
1030
1355
|
pageSize: params.limit || 50
|
|
1031
1356
|
});
|
|
1032
1357
|
return timeline.map((cast) => this.castToFarcasterCast(cast, params.agentId));
|
|
1033
1358
|
} catch (error) {
|
|
1034
|
-
|
|
1359
|
+
logger6.error(`Failed to get casts: ${JSON.stringify({ params, error })}`);
|
|
1035
1360
|
return [];
|
|
1036
1361
|
}
|
|
1037
1362
|
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Create a new cast
|
|
1040
|
-
*/
|
|
1041
1363
|
async createCast(params) {
|
|
1042
|
-
var _a;
|
|
1043
1364
|
try {
|
|
1044
1365
|
let castText = params.text;
|
|
1045
1366
|
if (!castText || castText.trim() === "") {
|
|
@@ -1050,7 +1371,7 @@ var FarcasterCastService = class {
|
|
|
1050
1371
|
}
|
|
1051
1372
|
const casts = await this.client.sendCast({
|
|
1052
1373
|
content: { text: castText },
|
|
1053
|
-
inReplyTo: params.replyTo ? { hash: params.replyTo.hash, fid: params.replyTo.fid } :
|
|
1374
|
+
inReplyTo: params.replyTo ? { hash: params.replyTo.hash, fid: params.replyTo.fid } : undefined
|
|
1054
1375
|
});
|
|
1055
1376
|
if (casts.length === 0) {
|
|
1056
1377
|
throw new Error("No cast was created");
|
|
@@ -1064,9 +1385,8 @@ var FarcasterCastService = class {
|
|
|
1064
1385
|
username: cast.profile.username,
|
|
1065
1386
|
text: cast.text,
|
|
1066
1387
|
timestamp: cast.timestamp.getTime(),
|
|
1067
|
-
inReplyTo:
|
|
1388
|
+
inReplyTo: params.replyTo?.hash,
|
|
1068
1389
|
media: [],
|
|
1069
|
-
// TODO: Handle media upload when Farcaster API supports it
|
|
1070
1390
|
metadata: {
|
|
1071
1391
|
castHash: cast.hash,
|
|
1072
1392
|
threadId: cast.threadId,
|
|
@@ -1077,73 +1397,60 @@ var FarcasterCastService = class {
|
|
|
1077
1397
|
await this.storeCastInMemory(params.roomId, farcasterCast);
|
|
1078
1398
|
return farcasterCast;
|
|
1079
1399
|
} catch (error) {
|
|
1080
|
-
|
|
1400
|
+
logger6.error(`Failed to create cast: ${JSON.stringify({ params, error })}`);
|
|
1081
1401
|
throw error;
|
|
1082
1402
|
}
|
|
1083
1403
|
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Delete a cast
|
|
1086
|
-
*/
|
|
1087
1404
|
async deleteCast(params) {
|
|
1088
1405
|
try {
|
|
1089
|
-
|
|
1406
|
+
logger6.warn(`Cast deletion is not supported by the Farcaster API: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1090
1407
|
} catch (error) {
|
|
1091
|
-
|
|
1408
|
+
logger6.error(`Failed to delete cast: ${JSON.stringify({ params, error })}`);
|
|
1092
1409
|
throw error;
|
|
1093
1410
|
}
|
|
1094
1411
|
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Like a cast
|
|
1097
|
-
*/
|
|
1098
1412
|
async likeCast(params) {
|
|
1099
1413
|
try {
|
|
1100
|
-
|
|
1414
|
+
logger6.info(`Like functionality not yet implemented for cast: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1101
1415
|
} catch (error) {
|
|
1102
|
-
|
|
1416
|
+
logger6.error(`Failed to like cast: ${JSON.stringify({ params, error })}`);
|
|
1103
1417
|
throw error;
|
|
1104
1418
|
}
|
|
1105
1419
|
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Unlike a cast
|
|
1108
|
-
*/
|
|
1109
1420
|
async unlikeCast(params) {
|
|
1110
1421
|
try {
|
|
1111
|
-
|
|
1422
|
+
logger6.info(`Unlike functionality not yet implemented for cast: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1112
1423
|
} catch (error) {
|
|
1113
|
-
|
|
1424
|
+
logger6.error(`Failed to unlike cast: ${JSON.stringify({ params, error })}`);
|
|
1114
1425
|
throw error;
|
|
1115
1426
|
}
|
|
1116
1427
|
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Recast a cast
|
|
1119
|
-
*/
|
|
1120
1428
|
async recast(params) {
|
|
1121
1429
|
try {
|
|
1122
|
-
|
|
1430
|
+
logger6.info(`Recast functionality not yet implemented for cast: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1123
1431
|
} catch (error) {
|
|
1124
|
-
|
|
1432
|
+
logger6.error(`Failed to recast: ${JSON.stringify({ params, error })}`);
|
|
1125
1433
|
throw error;
|
|
1126
1434
|
}
|
|
1127
1435
|
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Remove a recast
|
|
1130
|
-
*/
|
|
1131
1436
|
async unrecast(params) {
|
|
1132
1437
|
try {
|
|
1133
|
-
|
|
1438
|
+
logger6.info(`Remove recast functionality not yet implemented for cast: ${JSON.stringify({ castHash: params.castHash })}`);
|
|
1134
1439
|
} catch (error) {
|
|
1135
|
-
|
|
1440
|
+
logger6.error(`Failed to remove recast: ${JSON.stringify({ params, error })}`);
|
|
1136
1441
|
throw error;
|
|
1137
1442
|
}
|
|
1138
1443
|
}
|
|
1139
|
-
/**
|
|
1140
|
-
* Get mentions
|
|
1141
|
-
*/
|
|
1142
1444
|
async getMentions(params) {
|
|
1143
|
-
var _a, _b;
|
|
1144
1445
|
try {
|
|
1446
|
+
const fidStr = this.runtime.getSetting("FARCASTER_FID");
|
|
1447
|
+
const fid = fidStr ? parseInt(fidStr, 10) : NaN;
|
|
1448
|
+
if (!fid || isNaN(fid)) {
|
|
1449
|
+
logger6.error("FARCASTER_FID is not configured");
|
|
1450
|
+
return [];
|
|
1451
|
+
}
|
|
1145
1452
|
const mentions = await this.client.getMentions({
|
|
1146
|
-
fid
|
|
1453
|
+
fid,
|
|
1147
1454
|
pageSize: params.limit || 20
|
|
1148
1455
|
});
|
|
1149
1456
|
return mentions.map((castWithInteractions) => {
|
|
@@ -1151,13 +1458,10 @@ var FarcasterCastService = class {
|
|
|
1151
1458
|
return this.castToFarcasterCast(cast, params.agentId);
|
|
1152
1459
|
});
|
|
1153
1460
|
} catch (error) {
|
|
1154
|
-
|
|
1461
|
+
logger6.error(`Failed to get mentions: ${JSON.stringify({ params, error })}`);
|
|
1155
1462
|
return [];
|
|
1156
1463
|
}
|
|
1157
1464
|
}
|
|
1158
|
-
/**
|
|
1159
|
-
* Generate cast content using AI
|
|
1160
|
-
*/
|
|
1161
1465
|
async generateCastContent() {
|
|
1162
1466
|
const prompt = `Generate an interesting and engaging Farcaster cast. It should be conversational, authentic, and under 320 characters. Topics can include technology, AI, crypto, decentralized social media, or general observations about life.`;
|
|
1163
1467
|
try {
|
|
@@ -1167,13 +1471,10 @@ var FarcasterCastService = class {
|
|
|
1167
1471
|
});
|
|
1168
1472
|
return response;
|
|
1169
1473
|
} catch (error) {
|
|
1170
|
-
|
|
1171
|
-
return "Hello Farcaster! \
|
|
1474
|
+
logger6.error(`Failed to generate cast content: ${JSON.stringify({ error })}`);
|
|
1475
|
+
return "Hello Farcaster! \uD83D\uDC4B";
|
|
1172
1476
|
}
|
|
1173
1477
|
}
|
|
1174
|
-
/**
|
|
1175
|
-
* Truncate cast to fit character limit
|
|
1176
|
-
*/
|
|
1177
1478
|
async truncateCast(text) {
|
|
1178
1479
|
const prompt = `Shorten this text to under 320 characters while keeping the main message intact: "${text}"`;
|
|
1179
1480
|
try {
|
|
@@ -1187,22 +1488,18 @@ var FarcasterCastService = class {
|
|
|
1187
1488
|
}
|
|
1188
1489
|
return truncated;
|
|
1189
1490
|
} catch (error) {
|
|
1190
|
-
|
|
1491
|
+
logger6.error(`Failed to truncate cast: ${JSON.stringify({ error })}`);
|
|
1191
1492
|
return text.substring(0, 317) + "...";
|
|
1192
1493
|
}
|
|
1193
1494
|
}
|
|
1194
|
-
/**
|
|
1195
|
-
* Store cast in agent memory
|
|
1196
|
-
*/
|
|
1197
1495
|
async storeCastInMemory(roomId, cast) {
|
|
1198
|
-
var _a;
|
|
1199
1496
|
try {
|
|
1200
1497
|
const memory = {
|
|
1201
1498
|
id: createUniqueUuid4(this.runtime, cast.id),
|
|
1202
1499
|
agentId: this.runtime.agentId,
|
|
1203
1500
|
content: {
|
|
1204
1501
|
text: cast.text,
|
|
1205
|
-
castHash:
|
|
1502
|
+
castHash: cast.metadata?.castHash,
|
|
1206
1503
|
castId: cast.id,
|
|
1207
1504
|
author: cast.username,
|
|
1208
1505
|
timestamp: cast.timestamp
|
|
@@ -1216,15 +1513,12 @@ var FarcasterCastService = class {
|
|
|
1216
1513
|
} else if (this.runtime.memory && typeof this.runtime.memory.create === "function") {
|
|
1217
1514
|
await this.runtime.memory.create(memory);
|
|
1218
1515
|
} else {
|
|
1219
|
-
|
|
1516
|
+
logger6.warn("Memory storage method not available in runtime");
|
|
1220
1517
|
}
|
|
1221
1518
|
} catch (error) {
|
|
1222
|
-
|
|
1519
|
+
logger6.error(`Failed to store cast in memory: ${JSON.stringify({ error })}`);
|
|
1223
1520
|
}
|
|
1224
1521
|
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Convert internal Cast type to FarcasterCast
|
|
1227
|
-
*/
|
|
1228
1522
|
castToFarcasterCast(cast, agentId) {
|
|
1229
1523
|
return {
|
|
1230
1524
|
id: castUuid({ hash: cast.hash, agentId }),
|
|
@@ -1235,7 +1529,6 @@ var FarcasterCastService = class {
|
|
|
1235
1529
|
text: cast.text,
|
|
1236
1530
|
timestamp: cast.timestamp.getTime(),
|
|
1237
1531
|
media: [],
|
|
1238
|
-
// Farcaster casts can have embedded media but not in our Cast type
|
|
1239
1532
|
metadata: {
|
|
1240
1533
|
castHash: cast.hash,
|
|
1241
1534
|
threadId: cast.threadId,
|
|
@@ -1245,39 +1538,35 @@ var FarcasterCastService = class {
|
|
|
1245
1538
|
}
|
|
1246
1539
|
};
|
|
1247
1540
|
}
|
|
1248
|
-
}
|
|
1541
|
+
}
|
|
1249
1542
|
|
|
1250
1543
|
// src/service.ts
|
|
1251
|
-
|
|
1544
|
+
class FarcasterService extends Service {
|
|
1252
1545
|
static instance;
|
|
1253
|
-
managers =
|
|
1254
|
-
messageServices =
|
|
1255
|
-
castServices =
|
|
1256
|
-
// Properly implement serviceType for discoverability
|
|
1546
|
+
managers = new Map;
|
|
1547
|
+
messageServices = new Map;
|
|
1548
|
+
castServices = new Map;
|
|
1257
1549
|
static serviceType = FARCASTER_SERVICE_NAME;
|
|
1258
|
-
// Add service description
|
|
1259
1550
|
description = "Farcaster integration service for sending and receiving casts";
|
|
1260
1551
|
capabilityDescription = "The agent is able to send and receive messages on farcaster";
|
|
1261
1552
|
static getInstance() {
|
|
1262
|
-
if (!
|
|
1263
|
-
|
|
1553
|
+
if (!FarcasterService.instance) {
|
|
1554
|
+
FarcasterService.instance = new FarcasterService;
|
|
1264
1555
|
}
|
|
1265
|
-
return
|
|
1556
|
+
return FarcasterService.instance;
|
|
1266
1557
|
}
|
|
1267
|
-
// Required by ElizaOS Service base class
|
|
1268
1558
|
async initialize(runtime) {
|
|
1269
|
-
await
|
|
1559
|
+
await FarcasterService.start(runtime);
|
|
1270
1560
|
}
|
|
1271
|
-
// Called to start a single Farcaster service
|
|
1272
1561
|
static async start(runtime) {
|
|
1273
|
-
const service =
|
|
1562
|
+
const service = FarcasterService.getInstance();
|
|
1274
1563
|
let manager = service.managers.get(runtime.agentId);
|
|
1275
1564
|
if (manager) {
|
|
1276
|
-
|
|
1565
|
+
logger7.warn({ agentId: runtime.agentId }, "Farcaster service already started");
|
|
1277
1566
|
return service;
|
|
1278
1567
|
}
|
|
1279
1568
|
if (!hasFarcasterEnabled(runtime)) {
|
|
1280
|
-
|
|
1569
|
+
logger7.debug({ agentId: runtime.agentId }, "Farcaster service not enabled");
|
|
1281
1570
|
return service;
|
|
1282
1571
|
}
|
|
1283
1572
|
const farcasterConfig = validateFarcasterConfig(runtime);
|
|
@@ -1288,59 +1577,48 @@ var FarcasterService = class _FarcasterService extends Service {
|
|
|
1288
1577
|
service.messageServices.set(runtime.agentId, messageService);
|
|
1289
1578
|
service.castServices.set(runtime.agentId, castService);
|
|
1290
1579
|
await manager.start();
|
|
1291
|
-
|
|
1580
|
+
logger7.success({ agentId: runtime.agentId }, "Farcaster client started");
|
|
1292
1581
|
return service;
|
|
1293
1582
|
}
|
|
1294
|
-
// Called to stop a single Farcaster service
|
|
1295
1583
|
static async stop(runtime) {
|
|
1296
|
-
const service =
|
|
1584
|
+
const service = FarcasterService.getInstance();
|
|
1297
1585
|
let manager = service.managers.get(runtime.agentId);
|
|
1298
1586
|
if (manager) {
|
|
1299
1587
|
await manager.stop();
|
|
1300
1588
|
service.managers.delete(runtime.agentId);
|
|
1301
1589
|
service.messageServices.delete(runtime.agentId);
|
|
1302
1590
|
service.castServices.delete(runtime.agentId);
|
|
1303
|
-
|
|
1591
|
+
logger7.info({ agentId: runtime.agentId }, "Farcaster client stopped");
|
|
1304
1592
|
} else {
|
|
1305
|
-
|
|
1593
|
+
logger7.debug({ agentId: runtime.agentId }, "Farcaster service not running");
|
|
1306
1594
|
}
|
|
1307
1595
|
}
|
|
1308
|
-
// Called to stop all Farcaster services
|
|
1309
1596
|
async stop() {
|
|
1310
|
-
|
|
1597
|
+
logger7.debug("Stopping ALL Farcaster services");
|
|
1311
1598
|
for (const manager of Array.from(this.managers.values())) {
|
|
1312
1599
|
const agentId = manager.runtime.agentId;
|
|
1313
1600
|
try {
|
|
1314
|
-
await
|
|
1601
|
+
await FarcasterService.stop(manager.runtime);
|
|
1315
1602
|
} catch (error) {
|
|
1316
|
-
|
|
1603
|
+
logger7.error({ agentId, error }, "Error stopping Farcaster service");
|
|
1317
1604
|
}
|
|
1318
1605
|
}
|
|
1319
1606
|
}
|
|
1320
|
-
// Get the MessageService for a specific agent
|
|
1321
1607
|
getMessageService(agentId) {
|
|
1322
1608
|
return this.messageServices.get(agentId);
|
|
1323
1609
|
}
|
|
1324
|
-
/**
|
|
1325
|
-
* Get the PostService for a specific agent (for compatibility)
|
|
1326
|
-
* @deprecated Use getCastService() instead. Will be removed in a future major release.
|
|
1327
|
-
*/
|
|
1328
1610
|
getPostService(agentId) {
|
|
1329
1611
|
return this.castServices.get(agentId);
|
|
1330
1612
|
}
|
|
1331
|
-
// Get the CastService for a specific agent
|
|
1332
1613
|
getCastService(agentId) {
|
|
1333
1614
|
return this.castServices.get(agentId);
|
|
1334
1615
|
}
|
|
1335
|
-
// Add health check method
|
|
1336
1616
|
async healthCheck() {
|
|
1337
1617
|
const managerStatuses = {};
|
|
1338
1618
|
let overallHealthy = true;
|
|
1339
1619
|
for (const [agentId, manager] of Array.from(this.managers.entries())) {
|
|
1340
1620
|
try {
|
|
1341
|
-
const profile = await manager.client.getProfile(
|
|
1342
|
-
parseInt(manager.runtime.getSetting("FARCASTER_FID"))
|
|
1343
|
-
);
|
|
1621
|
+
const profile = await manager.client.getProfile(parseInt(manager.runtime.getSetting("FARCASTER_FID")));
|
|
1344
1622
|
managerStatuses[agentId] = {
|
|
1345
1623
|
status: "healthy",
|
|
1346
1624
|
fid: profile.fid,
|
|
@@ -1362,16 +1640,16 @@ var FarcasterService = class _FarcasterService extends Service {
|
|
|
1362
1640
|
}
|
|
1363
1641
|
};
|
|
1364
1642
|
}
|
|
1365
|
-
// Get all active managers (for monitoring)
|
|
1366
1643
|
getActiveManagers() {
|
|
1367
1644
|
return new Map(this.managers);
|
|
1368
1645
|
}
|
|
1369
|
-
}
|
|
1646
|
+
}
|
|
1370
1647
|
|
|
1371
1648
|
// src/__tests__/suite.ts
|
|
1649
|
+
init_constants();
|
|
1372
1650
|
import {
|
|
1373
1651
|
ModelType as ModelType3,
|
|
1374
|
-
createUniqueUuid as
|
|
1652
|
+
createUniqueUuid as createUniqueUuid7,
|
|
1375
1653
|
logger as logger10
|
|
1376
1654
|
} from "@elizaos/core";
|
|
1377
1655
|
|
|
@@ -1389,27 +1667,27 @@ var TEST_IMAGE = {
|
|
|
1389
1667
|
};
|
|
1390
1668
|
|
|
1391
1669
|
// src/__tests__/e2e/scenarios.ts
|
|
1392
|
-
|
|
1670
|
+
init_constants();
|
|
1671
|
+
import { logger as logger9, createUniqueUuid as createUniqueUuid6 } from "@elizaos/core";
|
|
1393
1672
|
var farcasterE2EScenarios = [
|
|
1394
1673
|
{
|
|
1395
1674
|
name: "Farcaster Plugin - Agent Introduction",
|
|
1396
1675
|
async fn(runtime) {
|
|
1397
|
-
var _a;
|
|
1398
1676
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1399
1677
|
if (!service) {
|
|
1400
1678
|
throw new Error("Farcaster service not initialized");
|
|
1401
1679
|
}
|
|
1402
|
-
const
|
|
1403
|
-
if (!
|
|
1404
|
-
throw new Error("
|
|
1680
|
+
const castService = service.getCastService(runtime.agentId);
|
|
1681
|
+
if (!castService) {
|
|
1682
|
+
throw new Error("CastService not available");
|
|
1405
1683
|
}
|
|
1406
|
-
const introText = `Hello Farcaster! I'm ${runtime.character.name}, an AI agent powered by ElizaOS. Looking forward to connecting with you all! \
|
|
1407
|
-
const cast = await
|
|
1684
|
+
const introText = `Hello Farcaster! I'm ${runtime.character.name}, an AI agent powered by ElizaOS. Looking forward to connecting with you all! \uD83E\uDD16`;
|
|
1685
|
+
const cast = await castService.createCast({
|
|
1408
1686
|
agentId: runtime.agentId,
|
|
1409
|
-
roomId:
|
|
1687
|
+
roomId: createUniqueUuid6(runtime, "farcaster-timeline"),
|
|
1410
1688
|
text: introText
|
|
1411
1689
|
});
|
|
1412
|
-
if (!cast || !cast.id || !cast.text || !
|
|
1690
|
+
if (!cast || !cast.id || !cast.text || !cast.metadata?.castHash) {
|
|
1413
1691
|
throw new Error("Failed to create introduction cast");
|
|
1414
1692
|
}
|
|
1415
1693
|
logger9.info(`Posted introduction cast: ${cast.metadata.castHash}`);
|
|
@@ -1432,11 +1710,11 @@ var farcasterE2EScenarios = [
|
|
|
1432
1710
|
if (!service) {
|
|
1433
1711
|
throw new Error("Farcaster service not initialized");
|
|
1434
1712
|
}
|
|
1435
|
-
const
|
|
1436
|
-
if (!
|
|
1437
|
-
throw new Error("
|
|
1713
|
+
const castService = service.getCastService(runtime.agentId);
|
|
1714
|
+
if (!castService) {
|
|
1715
|
+
throw new Error("CastService not available");
|
|
1438
1716
|
}
|
|
1439
|
-
const casts = await
|
|
1717
|
+
const casts = await castService.getCasts({
|
|
1440
1718
|
agentId: runtime.agentId,
|
|
1441
1719
|
limit: 10
|
|
1442
1720
|
});
|
|
@@ -1451,7 +1729,7 @@ var farcasterE2EScenarios = [
|
|
|
1451
1729
|
}
|
|
1452
1730
|
logger9.info(`Latest cast by @${firstCast.username}: ${firstCast.text.substring(0, 50)}...`);
|
|
1453
1731
|
}
|
|
1454
|
-
const mentions = await
|
|
1732
|
+
const mentions = await castService.getMentions({ agentId: runtime.agentId, limit: 5 });
|
|
1455
1733
|
if (!Array.isArray(mentions)) {
|
|
1456
1734
|
throw new Error("getMentions did not return an array");
|
|
1457
1735
|
}
|
|
@@ -1461,7 +1739,6 @@ var farcasterE2EScenarios = [
|
|
|
1461
1739
|
{
|
|
1462
1740
|
name: "Farcaster Plugin - Message Send and Retrieve",
|
|
1463
1741
|
async fn(runtime) {
|
|
1464
|
-
var _a;
|
|
1465
1742
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1466
1743
|
if (!service) {
|
|
1467
1744
|
throw new Error("Farcaster service not initialized");
|
|
@@ -1470,14 +1747,14 @@ var farcasterE2EScenarios = [
|
|
|
1470
1747
|
if (!messageService) {
|
|
1471
1748
|
throw new Error("MessageService not available");
|
|
1472
1749
|
}
|
|
1473
|
-
const roomId =
|
|
1750
|
+
const roomId = createUniqueUuid6(runtime, "test-conversation");
|
|
1474
1751
|
const message = await messageService.sendMessage({
|
|
1475
1752
|
agentId: runtime.agentId,
|
|
1476
1753
|
roomId,
|
|
1477
|
-
text: "Testing message send and retrieve with ElizaOS Farcaster plugin! \
|
|
1754
|
+
text: "Testing message send and retrieve with ElizaOS Farcaster plugin! \uD83E\uDDEA",
|
|
1478
1755
|
type: "CAST" /* CAST */
|
|
1479
1756
|
});
|
|
1480
|
-
if (!message || !message.id || !
|
|
1757
|
+
if (!message || !message.id || !message.metadata?.castHash) {
|
|
1481
1758
|
throw new Error("Failed to send message or missing metadata");
|
|
1482
1759
|
}
|
|
1483
1760
|
logger9.info(`Sent cast with hash: ${message.metadata.castHash}`);
|
|
@@ -1492,35 +1769,34 @@ var farcasterE2EScenarios = [
|
|
|
1492
1769
|
{
|
|
1493
1770
|
name: "Farcaster Plugin - Reply Threading",
|
|
1494
1771
|
async fn(runtime) {
|
|
1495
|
-
var _a, _b;
|
|
1496
1772
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1497
1773
|
if (!service) {
|
|
1498
1774
|
throw new Error("Farcaster service not initialized");
|
|
1499
1775
|
}
|
|
1500
1776
|
const messageService = service.getMessageService(runtime.agentId);
|
|
1501
|
-
const
|
|
1502
|
-
if (!messageService || !
|
|
1777
|
+
const castService = service.getCastService(runtime.agentId);
|
|
1778
|
+
if (!messageService || !castService) {
|
|
1503
1779
|
throw new Error("Services not available");
|
|
1504
1780
|
}
|
|
1505
|
-
const originalCast = await
|
|
1781
|
+
const originalCast = await castService.createCast({
|
|
1506
1782
|
agentId: runtime.agentId,
|
|
1507
|
-
roomId:
|
|
1508
|
-
text: "This is a test cast for reply threading \
|
|
1783
|
+
roomId: createUniqueUuid6(runtime, "reply-test"),
|
|
1784
|
+
text: "This is a test cast for reply threading \uD83E\uDDF5"
|
|
1509
1785
|
});
|
|
1510
|
-
if (!originalCast || !
|
|
1786
|
+
if (!originalCast || !originalCast.metadata?.castHash) {
|
|
1511
1787
|
throw new Error("Failed to create original cast");
|
|
1512
1788
|
}
|
|
1513
1789
|
const reply = await messageService.sendMessage({
|
|
1514
1790
|
agentId: runtime.agentId,
|
|
1515
1791
|
roomId: originalCast.roomId,
|
|
1516
|
-
text: "This is a test reply maintaining thread context! \
|
|
1792
|
+
text: "This is a test reply maintaining thread context! \uD83D\uDCAC",
|
|
1517
1793
|
type: "REPLY",
|
|
1518
1794
|
replyToId: originalCast.metadata.castHash,
|
|
1519
1795
|
metadata: {
|
|
1520
1796
|
parentHash: originalCast.metadata.castHash
|
|
1521
1797
|
}
|
|
1522
1798
|
});
|
|
1523
|
-
if (!reply || !reply.inReplyTo || !
|
|
1799
|
+
if (!reply || !reply.inReplyTo || !reply.metadata?.castHash) {
|
|
1524
1800
|
throw new Error("Failed to create reply or missing thread context");
|
|
1525
1801
|
}
|
|
1526
1802
|
logger9.info(`Created reply ${reply.metadata.castHash} to ${originalCast.metadata.castHash}`);
|
|
@@ -1529,11 +1805,11 @@ var farcasterE2EScenarios = [
|
|
|
1529
1805
|
{
|
|
1530
1806
|
name: "Farcaster Plugin - Action Execution",
|
|
1531
1807
|
async fn(runtime) {
|
|
1532
|
-
const { sendCastAction: sendCastAction2 } = await
|
|
1808
|
+
const { sendCastAction: sendCastAction2 } = await Promise.resolve().then(() => (init_sendCast(), exports_sendCast));
|
|
1533
1809
|
const mockMessage = {
|
|
1534
|
-
id:
|
|
1810
|
+
id: createUniqueUuid6(runtime, "test-message"),
|
|
1535
1811
|
agentId: runtime.agentId,
|
|
1536
|
-
roomId:
|
|
1812
|
+
roomId: createUniqueUuid6(runtime, "test-room"),
|
|
1537
1813
|
entityId: runtime.agentId,
|
|
1538
1814
|
content: {
|
|
1539
1815
|
text: "Can you post about the ElizaOS framework on Farcaster?"
|
|
@@ -1544,34 +1820,30 @@ var farcasterE2EScenarios = [
|
|
|
1544
1820
|
if (!shouldExecute) {
|
|
1545
1821
|
throw new Error("SEND_CAST action validation failed");
|
|
1546
1822
|
}
|
|
1547
|
-
|
|
1548
|
-
if (!result) {
|
|
1549
|
-
throw new Error("SEND_CAST action execution failed");
|
|
1550
|
-
}
|
|
1823
|
+
await sendCastAction2.handler(runtime, mockMessage);
|
|
1551
1824
|
logger9.info("Successfully validated and executed SEND_CAST action");
|
|
1552
1825
|
}
|
|
1553
1826
|
},
|
|
1554
1827
|
{
|
|
1555
1828
|
name: "Farcaster Plugin - Provider Context",
|
|
1556
1829
|
async fn(runtime) {
|
|
1557
|
-
|
|
1558
|
-
const {
|
|
1559
|
-
const { farcasterTimelineProvider: farcasterTimelineProvider2 } = await import("./timelineProvider-GPRPFEVJ.js");
|
|
1830
|
+
const { farcasterProfileProvider: farcasterProfileProvider2 } = await Promise.resolve().then(() => (init_profileProvider(), exports_profileProvider));
|
|
1831
|
+
const { farcasterTimelineProvider: farcasterTimelineProvider2 } = await Promise.resolve().then(() => (init_timelineProvider(), exports_timelineProvider));
|
|
1560
1832
|
const mockMessage = {
|
|
1561
|
-
id:
|
|
1833
|
+
id: createUniqueUuid6(runtime, "test-message"),
|
|
1562
1834
|
agentId: runtime.agentId,
|
|
1563
|
-
roomId:
|
|
1835
|
+
roomId: createUniqueUuid6(runtime, "test-room"),
|
|
1564
1836
|
entityId: runtime.agentId,
|
|
1565
1837
|
content: { text: "test" },
|
|
1566
1838
|
createdAt: Date.now()
|
|
1567
1839
|
};
|
|
1568
1840
|
const profileContext = await farcasterProfileProvider2.get(runtime, mockMessage, { values: [], data: {}, text: "" });
|
|
1569
|
-
if (!profileContext || !profileContext.text ||
|
|
1841
|
+
if (!profileContext || !profileContext.text || profileContext.data?.available === undefined) {
|
|
1570
1842
|
throw new Error("Profile provider returned invalid context");
|
|
1571
1843
|
}
|
|
1572
1844
|
logger9.info(`Profile provider: ${profileContext.text}`);
|
|
1573
1845
|
const timelineContext = await farcasterTimelineProvider2.get(runtime, mockMessage, { values: [], data: {}, text: "" });
|
|
1574
|
-
if (!timelineContext || !timelineContext.text ||
|
|
1846
|
+
if (!timelineContext || !timelineContext.text || timelineContext.data?.available === undefined) {
|
|
1575
1847
|
throw new Error("Timeline provider returned invalid context");
|
|
1576
1848
|
}
|
|
1577
1849
|
logger9.info(`Timeline provider: ${timelineContext.text}`);
|
|
@@ -1589,18 +1861,16 @@ var farcasterE2EScenarios = [
|
|
|
1589
1861
|
throw new Error("MessageService not available");
|
|
1590
1862
|
}
|
|
1591
1863
|
const promises = [];
|
|
1592
|
-
for (let i = 0;
|
|
1593
|
-
promises.push(
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
})
|
|
1603
|
-
);
|
|
1864
|
+
for (let i = 0;i < 3; i++) {
|
|
1865
|
+
promises.push(messageService.sendMessage({
|
|
1866
|
+
agentId: runtime.agentId,
|
|
1867
|
+
roomId: createUniqueUuid6(runtime, "rate-limit-test"),
|
|
1868
|
+
text: `Rate limit test message ${i + 1}`,
|
|
1869
|
+
type: "CAST" /* CAST */
|
|
1870
|
+
}).catch((error) => {
|
|
1871
|
+
logger9.warn(`Expected rate limit error: ${error.message}`);
|
|
1872
|
+
return null;
|
|
1873
|
+
}));
|
|
1604
1874
|
}
|
|
1605
1875
|
const results = await Promise.all(promises);
|
|
1606
1876
|
const successfulSends = results.filter((r) => r !== null);
|
|
@@ -1618,13 +1888,13 @@ var farcasterE2EScenarios = [
|
|
|
1618
1888
|
throw new Error("Farcaster service not initialized");
|
|
1619
1889
|
}
|
|
1620
1890
|
const health = await service.healthCheck();
|
|
1621
|
-
if (!health || health.healthy ===
|
|
1891
|
+
if (!health || health.healthy === undefined || !health.details) {
|
|
1622
1892
|
throw new Error("Health check returned invalid data");
|
|
1623
1893
|
}
|
|
1624
1894
|
logger9.info(`Service health: ${health.healthy ? "Healthy" : "Unhealthy"}`);
|
|
1625
1895
|
logger9.info(`Active managers: ${health.details.activeManagers}`);
|
|
1626
1896
|
if (!health.healthy) {
|
|
1627
|
-
logger9.warn("Service reported unhealthy status
|
|
1897
|
+
logger9.warn({ details: health.details }, "Service reported unhealthy status");
|
|
1628
1898
|
}
|
|
1629
1899
|
}
|
|
1630
1900
|
},
|
|
@@ -1635,15 +1905,15 @@ var farcasterE2EScenarios = [
|
|
|
1635
1905
|
if (!service) {
|
|
1636
1906
|
throw new Error("FarcasterService not found");
|
|
1637
1907
|
}
|
|
1638
|
-
const
|
|
1639
|
-
if (!
|
|
1640
|
-
throw new Error("
|
|
1908
|
+
const castService = service.getCastService(runtime.agentId);
|
|
1909
|
+
if (!castService) {
|
|
1910
|
+
throw new Error("CastService not available");
|
|
1641
1911
|
}
|
|
1642
|
-
const uniqueMessage = `This is a real E2E test cast from ElizaOS! ID: ${
|
|
1912
|
+
const uniqueMessage = `This is a real E2E test cast from ElizaOS! ID: ${createUniqueUuid6(runtime, "e2e-cast")}`;
|
|
1643
1913
|
logger9.info(`Attempting to post cast: "${uniqueMessage}"`);
|
|
1644
|
-
const cast = await
|
|
1914
|
+
const cast = await castService.createCast({
|
|
1645
1915
|
agentId: runtime.agentId,
|
|
1646
|
-
roomId:
|
|
1916
|
+
roomId: createUniqueUuid6(runtime, "farcaster-e2e-test"),
|
|
1647
1917
|
text: uniqueMessage
|
|
1648
1918
|
});
|
|
1649
1919
|
if (!cast || !cast.id) {
|
|
@@ -1655,30 +1925,26 @@ var farcasterE2EScenarios = [
|
|
|
1655
1925
|
];
|
|
1656
1926
|
|
|
1657
1927
|
// src/__tests__/suite.ts
|
|
1658
|
-
|
|
1928
|
+
class FarcasterTestSuite {
|
|
1659
1929
|
name = "Farcaster Plugin Tests";
|
|
1660
1930
|
description = "Test suite for Farcaster plugin functionality";
|
|
1661
1931
|
manager = null;
|
|
1662
1932
|
tests;
|
|
1663
|
-
/**
|
|
1664
|
-
* Constructor for TestSuite class.
|
|
1665
|
-
* Initializes an array of test functions to be executed.
|
|
1666
|
-
*/
|
|
1667
1933
|
constructor() {
|
|
1668
1934
|
const hasRealCredentials = !!(process.env.FARCASTER_FID && process.env.FARCASTER_SIGNER_UUID && process.env.FARCASTER_NEYNAR_API_KEY);
|
|
1669
1935
|
logger10.info("=== Farcaster Test Suite Configuration ===");
|
|
1670
|
-
logger10.info(`FID: ${process.env.FARCASTER_FID ? "
|
|
1671
|
-
logger10.info(`Signer UUID: ${process.env.FARCASTER_SIGNER_UUID ? "
|
|
1672
|
-
logger10.info(`API Key: ${process.env.FARCASTER_NEYNAR_API_KEY ? "
|
|
1936
|
+
logger10.info(`FID: ${process.env.FARCASTER_FID ? "✓ Found" : "✗ Missing"}`);
|
|
1937
|
+
logger10.info(`Signer UUID: ${process.env.FARCASTER_SIGNER_UUID ? "✓ Found" : "✗ Missing"}`);
|
|
1938
|
+
logger10.info(`API Key: ${process.env.FARCASTER_NEYNAR_API_KEY ? "✓ Found" : "✗ Missing"}`);
|
|
1673
1939
|
logger10.info(`Dry Run: ${process.env.FARCASTER_DRY_RUN || "not set (defaults to false)"}`);
|
|
1674
1940
|
logger10.info("=========================================");
|
|
1675
1941
|
if (hasRealCredentials) {
|
|
1676
|
-
logger10.success("
|
|
1942
|
+
logger10.success("✅ Running with real Farcaster credentials");
|
|
1677
1943
|
this.tests = farcasterE2EScenarios;
|
|
1678
1944
|
} else {
|
|
1679
|
-
logger10.warn("
|
|
1680
|
-
logger10.warn("
|
|
1681
|
-
logger10.warn("
|
|
1945
|
+
logger10.warn("⚠️ Farcaster credentials not found in environment variables");
|
|
1946
|
+
logger10.warn("⚠️ Tests will run in mock mode");
|
|
1947
|
+
logger10.warn("⚠️ To run real tests, set FARCASTER_FID, FARCASTER_SIGNER_UUID, and FARCASTER_NEYNAR_API_KEY");
|
|
1682
1948
|
this.tests = [
|
|
1683
1949
|
{
|
|
1684
1950
|
name: "Mock: Check Farcaster Configuration",
|
|
@@ -1691,9 +1957,6 @@ var FarcasterTestSuite = class {
|
|
|
1691
1957
|
];
|
|
1692
1958
|
}
|
|
1693
1959
|
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Test that checks if Farcaster is properly configured
|
|
1696
|
-
*/
|
|
1697
1960
|
async testMockConfiguration(runtime) {
|
|
1698
1961
|
logger10.info("Running mock configuration test");
|
|
1699
1962
|
const fid = runtime.getSetting("FARCASTER_FID");
|
|
@@ -1711,9 +1974,6 @@ var FarcasterTestSuite = class {
|
|
|
1711
1974
|
logger10.info("- FARCASTER_NEYNAR_API_KEY: Neynar API key");
|
|
1712
1975
|
}
|
|
1713
1976
|
}
|
|
1714
|
-
/**
|
|
1715
|
-
* Test service initialization without real credentials
|
|
1716
|
-
*/
|
|
1717
1977
|
async testMockServiceInit(runtime) {
|
|
1718
1978
|
logger10.info("Running mock service initialization test");
|
|
1719
1979
|
try {
|
|
@@ -1724,24 +1984,18 @@ var FarcasterTestSuite = class {
|
|
|
1724
1984
|
}
|
|
1725
1985
|
logger10.info("Farcaster service is registered but may not be fully initialized");
|
|
1726
1986
|
if (typeof service.getMessageService === "function") {
|
|
1727
|
-
logger10.info("
|
|
1987
|
+
logger10.info("✓ getMessageService method exists");
|
|
1728
1988
|
}
|
|
1729
1989
|
if (typeof service.getPostService === "function") {
|
|
1730
|
-
logger10.info("
|
|
1990
|
+
logger10.info("✓ getPostService method exists");
|
|
1731
1991
|
}
|
|
1732
1992
|
if (typeof service.healthCheck === "function") {
|
|
1733
|
-
logger10.info("
|
|
1993
|
+
logger10.info("✓ healthCheck method exists");
|
|
1734
1994
|
}
|
|
1735
1995
|
} catch (error) {
|
|
1736
1996
|
logger10.info("Service initialization check completed");
|
|
1737
1997
|
}
|
|
1738
1998
|
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Asynchronously initializes the Farcaster client for the provided agent runtime.
|
|
1741
|
-
*
|
|
1742
|
-
* @param {IAgentRuntime} runtime - The agent runtime to use for initializing the Farcaster client.
|
|
1743
|
-
* @throws {Error} If the Farcaster client manager is not found or if the Farcaster client fails to initialize.
|
|
1744
|
-
*/
|
|
1745
1999
|
async testInitializingClient(runtime) {
|
|
1746
2000
|
try {
|
|
1747
2001
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
@@ -1758,12 +2012,6 @@ var FarcasterTestSuite = class {
|
|
|
1758
2012
|
throw new Error(`Error in initializing Farcaster client: ${error}`);
|
|
1759
2013
|
}
|
|
1760
2014
|
}
|
|
1761
|
-
/**
|
|
1762
|
-
* Asynchronously fetches the profile of a user from Farcaster using the given runtime.
|
|
1763
|
-
*
|
|
1764
|
-
* @param {IAgentRuntime} runtime The runtime to use for fetching the profile.
|
|
1765
|
-
* @returns {Promise<void>} A Promise that resolves when the profile is successfully fetched, or rejects with an error.
|
|
1766
|
-
*/
|
|
1767
2015
|
async testFetchProfile(runtime) {
|
|
1768
2016
|
try {
|
|
1769
2017
|
if (!this.manager) {
|
|
@@ -1777,18 +2025,11 @@ var FarcasterTestSuite = class {
|
|
|
1777
2025
|
if (!profile || !profile.fid) {
|
|
1778
2026
|
throw new Error("Profile fetch failed.");
|
|
1779
2027
|
}
|
|
1780
|
-
|
|
2028
|
+
runtime.logger.info({ profile }, "Successfully fetched Farcaster profile:");
|
|
1781
2029
|
} catch (error) {
|
|
1782
2030
|
throw new Error(`Error fetching Farcaster profile: ${error}`);
|
|
1783
2031
|
}
|
|
1784
2032
|
}
|
|
1785
|
-
/**
|
|
1786
|
-
* Asynchronously fetches the timeline from the Farcaster client.
|
|
1787
|
-
*
|
|
1788
|
-
* @param {IAgentRuntime} runtime - The agent runtime object.
|
|
1789
|
-
* @throws {Error} If there are no casts in the timeline.
|
|
1790
|
-
* @throws {Error} If an error occurs while fetching the timeline.
|
|
1791
|
-
*/
|
|
1792
2033
|
async testFetchTimeline(runtime) {
|
|
1793
2034
|
try {
|
|
1794
2035
|
if (!this.manager) {
|
|
@@ -1808,13 +2049,6 @@ var FarcasterTestSuite = class {
|
|
|
1808
2049
|
throw new Error(`Error fetching timeline: ${error}`);
|
|
1809
2050
|
}
|
|
1810
2051
|
}
|
|
1811
|
-
/**
|
|
1812
|
-
* Asynchronously posts a test cast using the Farcaster API.
|
|
1813
|
-
*
|
|
1814
|
-
* @param {IAgentRuntime} runtime - The agent runtime object.
|
|
1815
|
-
* @returns {Promise<void>} A Promise that resolves when the cast is successfully posted.
|
|
1816
|
-
* @throws {Error} If there is an error posting the cast.
|
|
1817
|
-
*/
|
|
1818
2052
|
async testPostCast(runtime) {
|
|
1819
2053
|
try {
|
|
1820
2054
|
if (!this.manager) {
|
|
@@ -1832,14 +2066,6 @@ var FarcasterTestSuite = class {
|
|
|
1832
2066
|
throw new Error(`Error posting a cast: ${error}`);
|
|
1833
2067
|
}
|
|
1834
2068
|
}
|
|
1835
|
-
/**
|
|
1836
|
-
* Asynchronously posts an image cast on Farcaster using the provided runtime and cast content.
|
|
1837
|
-
* Note: This might need updating based on how images are actually handled in sendCast
|
|
1838
|
-
*
|
|
1839
|
-
* @param {IAgentRuntime} runtime - The runtime environment for the action.
|
|
1840
|
-
* @returns {Promise<void>} A Promise that resolves when the cast is successfully posted.
|
|
1841
|
-
* @throws {Error} If there is an error posting the cast.
|
|
1842
|
-
*/
|
|
1843
2069
|
async testPostImageCast(runtime) {
|
|
1844
2070
|
try {
|
|
1845
2071
|
if (!this.manager) {
|
|
@@ -1860,13 +2086,6 @@ var FarcasterTestSuite = class {
|
|
|
1860
2086
|
throw new Error(`Error posting a cast with image: ${error}`);
|
|
1861
2087
|
}
|
|
1862
2088
|
}
|
|
1863
|
-
/**
|
|
1864
|
-
* Asynchronously handles a fake cast response using the given runtime.
|
|
1865
|
-
*
|
|
1866
|
-
* @param {IAgentRuntime} runtime - The runtime object for the agent
|
|
1867
|
-
* @returns {Promise<void>} - A promise that resolves when the cast response is handled
|
|
1868
|
-
* @throws {Error} - If there is an error handling the cast response
|
|
1869
|
-
*/
|
|
1870
2089
|
async testHandleCastResponse(runtime) {
|
|
1871
2090
|
try {
|
|
1872
2091
|
if (!this.manager) {
|
|
@@ -1881,17 +2100,17 @@ var FarcasterTestSuite = class {
|
|
|
1881
2100
|
username: "randomUser",
|
|
1882
2101
|
name: "Random User"
|
|
1883
2102
|
},
|
|
1884
|
-
timestamp:
|
|
2103
|
+
timestamp: new Date
|
|
1885
2104
|
};
|
|
1886
|
-
const memoryId =
|
|
2105
|
+
const memoryId = createUniqueUuid7(runtime, testCast.hash);
|
|
1887
2106
|
const memory = {
|
|
1888
2107
|
id: memoryId,
|
|
1889
2108
|
agentId: runtime.agentId,
|
|
1890
2109
|
content: {
|
|
1891
2110
|
text: testCast.text
|
|
1892
2111
|
},
|
|
1893
|
-
entityId:
|
|
1894
|
-
roomId:
|
|
2112
|
+
entityId: createUniqueUuid7(runtime, String(testCast.authorFid)),
|
|
2113
|
+
roomId: createUniqueUuid7(runtime, "test-room"),
|
|
1895
2114
|
createdAt: testCast.timestamp.getTime()
|
|
1896
2115
|
};
|
|
1897
2116
|
runtime.emitEvent("farcaster.mention_received", {
|
|
@@ -1905,13 +2124,6 @@ var FarcasterTestSuite = class {
|
|
|
1905
2124
|
throw new Error(`Error handling cast response: ${error}`);
|
|
1906
2125
|
}
|
|
1907
2126
|
}
|
|
1908
|
-
/**
|
|
1909
|
-
* Generates random content for a cast based on the given context.
|
|
1910
|
-
*
|
|
1911
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
1912
|
-
* @param {string} context - Optional context for the content generation.
|
|
1913
|
-
* @returns {Promise<string>} A promise that resolves to the generated cast content.
|
|
1914
|
-
*/
|
|
1915
2127
|
async generateRandomCastContent(runtime, context = "general") {
|
|
1916
2128
|
const prompt = `Generate a short, interesting cast about ${context} (max 280 chars).`;
|
|
1917
2129
|
const result = await runtime.useModel(ModelType3.TEXT_SMALL, {
|
|
@@ -1919,12 +2131,6 @@ var FarcasterTestSuite = class {
|
|
|
1919
2131
|
});
|
|
1920
2132
|
return result.substring(0, 280);
|
|
1921
2133
|
}
|
|
1922
|
-
/**
|
|
1923
|
-
* Tests the MessageService functionality
|
|
1924
|
-
*
|
|
1925
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
1926
|
-
* @returns {Promise<void>} A promise that resolves when the test is complete.
|
|
1927
|
-
*/
|
|
1928
2134
|
async testMessageService(runtime) {
|
|
1929
2135
|
try {
|
|
1930
2136
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
@@ -1943,7 +2149,7 @@ var FarcasterTestSuite = class {
|
|
|
1943
2149
|
const testText = await this.generateRandomCastContent(runtime, "message_service_test");
|
|
1944
2150
|
const message = await messageService.sendMessage({
|
|
1945
2151
|
agentId: runtime.agentId,
|
|
1946
|
-
roomId:
|
|
2152
|
+
roomId: createUniqueUuid7(runtime, "test-room"),
|
|
1947
2153
|
text: testText,
|
|
1948
2154
|
type: "CAST" /* CAST */
|
|
1949
2155
|
});
|
|
@@ -1955,12 +2161,6 @@ var FarcasterTestSuite = class {
|
|
|
1955
2161
|
throw new Error(`Error testing MessageService: ${error}`);
|
|
1956
2162
|
}
|
|
1957
2163
|
}
|
|
1958
|
-
/**
|
|
1959
|
-
* Tests the PostService functionality
|
|
1960
|
-
*
|
|
1961
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
1962
|
-
* @returns {Promise<void>} A promise that resolves when the test is complete.
|
|
1963
|
-
*/
|
|
1964
2164
|
async testPostService(runtime) {
|
|
1965
2165
|
try {
|
|
1966
2166
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
@@ -1979,7 +2179,7 @@ var FarcasterTestSuite = class {
|
|
|
1979
2179
|
const testText = await this.generateRandomCastContent(runtime, "cast_service_test");
|
|
1980
2180
|
const cast = await castService.createCast({
|
|
1981
2181
|
agentId: runtime.agentId,
|
|
1982
|
-
roomId:
|
|
2182
|
+
roomId: createUniqueUuid7(runtime, "test-room"),
|
|
1983
2183
|
text: testText
|
|
1984
2184
|
});
|
|
1985
2185
|
if (!cast || !cast.id) {
|
|
@@ -1990,14 +2190,7 @@ var FarcasterTestSuite = class {
|
|
|
1990
2190
|
throw new Error(`Error testing PostService: ${error}`);
|
|
1991
2191
|
}
|
|
1992
2192
|
}
|
|
1993
|
-
/**
|
|
1994
|
-
* Tests real account posting functionality
|
|
1995
|
-
*
|
|
1996
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
1997
|
-
* @returns {Promise<void>} A promise that resolves when the test is complete.
|
|
1998
|
-
*/
|
|
1999
2193
|
async testRealAccountPosting(runtime) {
|
|
2000
|
-
var _a;
|
|
2001
2194
|
try {
|
|
2002
2195
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2003
2196
|
if (!service) {
|
|
@@ -2008,35 +2201,28 @@ var FarcasterTestSuite = class {
|
|
|
2008
2201
|
throw new Error("PostService not initialized");
|
|
2009
2202
|
}
|
|
2010
2203
|
const testPosts = [
|
|
2011
|
-
"Testing ElizaOS Farcaster integration! \
|
|
2012
|
-
"AI agents are the future of social media engagement \
|
|
2013
|
-
"Building amazing things with the ElizaOS framework \
|
|
2204
|
+
"Testing ElizaOS Farcaster integration! \uD83D\uDE80 #ElizaOS",
|
|
2205
|
+
"AI agents are the future of social media engagement \uD83E\uDD16",
|
|
2206
|
+
"Building amazing things with the ElizaOS framework \uD83D\uDCBB"
|
|
2014
2207
|
];
|
|
2015
2208
|
for (const text of testPosts) {
|
|
2016
2209
|
const post = await postService.createPost({
|
|
2017
2210
|
agentId: runtime.agentId,
|
|
2018
|
-
roomId:
|
|
2211
|
+
roomId: createUniqueUuid7(runtime, "real-test"),
|
|
2019
2212
|
text
|
|
2020
2213
|
});
|
|
2021
|
-
if (!post || !post.id || !
|
|
2214
|
+
if (!post || !post.id || !post.metadata?.castHash) {
|
|
2022
2215
|
throw new Error("Failed to create real post");
|
|
2023
2216
|
}
|
|
2024
2217
|
logger10.success(`Posted real cast: ${post.metadata.castHash}`);
|
|
2025
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
2218
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2026
2219
|
}
|
|
2027
2220
|
logger10.success("Real account posting test completed successfully");
|
|
2028
2221
|
} catch (error) {
|
|
2029
2222
|
throw new Error(`Error testing real account posting: ${error}`);
|
|
2030
2223
|
}
|
|
2031
2224
|
}
|
|
2032
|
-
/**
|
|
2033
|
-
* Tests real account interactions (mentions, replies, etc)
|
|
2034
|
-
*
|
|
2035
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
2036
|
-
* @returns {Promise<void>} A promise that resolves when the test is complete.
|
|
2037
|
-
*/
|
|
2038
2225
|
async testRealAccountInteractions(runtime) {
|
|
2039
|
-
var _a, _b;
|
|
2040
2226
|
try {
|
|
2041
2227
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2042
2228
|
if (!service) {
|
|
@@ -2056,18 +2242,18 @@ var FarcasterTestSuite = class {
|
|
|
2056
2242
|
logger10.log(`Found ${timeline.length} timeline posts`);
|
|
2057
2243
|
if (timeline.length > 0) {
|
|
2058
2244
|
const targetPost = timeline[0];
|
|
2059
|
-
if (
|
|
2245
|
+
if (targetPost.metadata?.castHash) {
|
|
2060
2246
|
const reply = await messageService.sendMessage({
|
|
2061
2247
|
agentId: runtime.agentId,
|
|
2062
2248
|
roomId: targetPost.roomId,
|
|
2063
|
-
text: "Great post! Testing real interactions with ElizaOS \
|
|
2249
|
+
text: "Great post! Testing real interactions with ElizaOS \uD83C\uDF89",
|
|
2064
2250
|
type: "REPLY" /* REPLY */,
|
|
2065
2251
|
replyToId: targetPost.metadata.castHash,
|
|
2066
2252
|
metadata: {
|
|
2067
2253
|
parentHash: targetPost.metadata.castHash
|
|
2068
2254
|
}
|
|
2069
2255
|
});
|
|
2070
|
-
if (reply &&
|
|
2256
|
+
if (reply && reply.metadata?.castHash) {
|
|
2071
2257
|
logger10.success(`Replied to cast with: ${reply.metadata.castHash}`);
|
|
2072
2258
|
}
|
|
2073
2259
|
}
|
|
@@ -2077,14 +2263,7 @@ var FarcasterTestSuite = class {
|
|
|
2077
2263
|
throw new Error(`Error testing real account interactions: ${error}`);
|
|
2078
2264
|
}
|
|
2079
2265
|
}
|
|
2080
|
-
/**
|
|
2081
|
-
* Tests message metadata tracking functionality
|
|
2082
|
-
*
|
|
2083
|
-
* @param {IAgentRuntime} runtime - The runtime environment.
|
|
2084
|
-
* @returns {Promise<void>} A promise that resolves when the test is complete.
|
|
2085
|
-
*/
|
|
2086
2266
|
async testMessageMetadataTracking(runtime) {
|
|
2087
|
-
var _a, _b;
|
|
2088
2267
|
try {
|
|
2089
2268
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2090
2269
|
if (!service) {
|
|
@@ -2096,11 +2275,11 @@ var FarcasterTestSuite = class {
|
|
|
2096
2275
|
}
|
|
2097
2276
|
const testMessage = await messageService.sendMessage({
|
|
2098
2277
|
agentId: runtime.agentId,
|
|
2099
|
-
roomId:
|
|
2278
|
+
roomId: createUniqueUuid7(runtime, "metadata-test"),
|
|
2100
2279
|
text: "Testing metadata tracking with ElizaOS",
|
|
2101
2280
|
type: "CAST" /* CAST */
|
|
2102
2281
|
});
|
|
2103
|
-
if (!testMessage || !
|
|
2282
|
+
if (!testMessage || !testMessage.metadata?.castHash) {
|
|
2104
2283
|
throw new Error("Failed to send test message");
|
|
2105
2284
|
}
|
|
2106
2285
|
const castHash = testMessage.metadata.castHash;
|
|
@@ -2109,7 +2288,7 @@ var FarcasterTestSuite = class {
|
|
|
2109
2288
|
if (!retrievedMessage) {
|
|
2110
2289
|
throw new Error("Failed to retrieve message by hash");
|
|
2111
2290
|
}
|
|
2112
|
-
if (
|
|
2291
|
+
if (retrievedMessage.metadata?.castHash !== castHash) {
|
|
2113
2292
|
throw new Error("Metadata mismatch in retrieved message");
|
|
2114
2293
|
}
|
|
2115
2294
|
logger10.success("Message metadata tracking test completed successfully");
|
|
@@ -2117,12 +2296,13 @@ var FarcasterTestSuite = class {
|
|
|
2117
2296
|
throw new Error(`Error testing message metadata tracking: ${error}`);
|
|
2118
2297
|
}
|
|
2119
2298
|
}
|
|
2120
|
-
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// src/actions/index.ts
|
|
2302
|
+
init_sendCast();
|
|
2121
2303
|
|
|
2122
2304
|
// src/actions/replyCast.ts
|
|
2123
|
-
|
|
2124
|
-
logger as logger11
|
|
2125
|
-
} from "@elizaos/core";
|
|
2305
|
+
init_constants();
|
|
2126
2306
|
var replyCastAction = {
|
|
2127
2307
|
name: "REPLY_TO_CAST",
|
|
2128
2308
|
description: "Replies to a cast on Farcaster",
|
|
@@ -2155,31 +2335,29 @@ var replyCastAction = {
|
|
|
2155
2335
|
]
|
|
2156
2336
|
],
|
|
2157
2337
|
validate: async (runtime, message) => {
|
|
2158
|
-
|
|
2159
|
-
const text = ((_a = message.content.text) == null ? void 0 : _a.toLowerCase()) || "";
|
|
2338
|
+
const text = message.content.text?.toLowerCase() || "";
|
|
2160
2339
|
const keywords = ["reply", "respond", "answer", "comment"];
|
|
2161
2340
|
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
2162
2341
|
const hasParentCast = !!(message.content.metadata && message.content.metadata.parentCastHash);
|
|
2163
2342
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2164
|
-
const isServiceAvailable = !!
|
|
2343
|
+
const isServiceAvailable = !!service?.getMessageService(runtime.agentId);
|
|
2165
2344
|
return hasKeyword && (hasParentCast || isServiceAvailable);
|
|
2166
2345
|
},
|
|
2167
2346
|
handler: async (runtime, message, state) => {
|
|
2168
|
-
var _a;
|
|
2169
2347
|
try {
|
|
2170
2348
|
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2171
|
-
const messageService = service
|
|
2349
|
+
const messageService = service?.getMessageService(runtime.agentId);
|
|
2172
2350
|
if (!messageService) {
|
|
2173
|
-
|
|
2174
|
-
return
|
|
2351
|
+
runtime.logger.error("[REPLY_TO_CAST] MessageService not available");
|
|
2352
|
+
return;
|
|
2175
2353
|
}
|
|
2176
|
-
const parentCastHash =
|
|
2354
|
+
const parentCastHash = message.content.metadata?.parentCastHash || state?.parentCastHash;
|
|
2177
2355
|
if (!parentCastHash) {
|
|
2178
|
-
|
|
2179
|
-
return
|
|
2356
|
+
runtime.logger.error("[REPLY_TO_CAST] No parent cast to reply to");
|
|
2357
|
+
return;
|
|
2180
2358
|
}
|
|
2181
2359
|
let replyContent = "";
|
|
2182
|
-
if (state
|
|
2360
|
+
if (state?.replyContent) {
|
|
2183
2361
|
replyContent = state.replyContent;
|
|
2184
2362
|
} else {
|
|
2185
2363
|
const prompt = `Based on this request: "${message.content.text}", generate a helpful and engaging reply for a Farcaster cast (max 320 characters).`;
|
|
@@ -2199,20 +2377,151 @@ var replyCastAction = {
|
|
|
2199
2377
|
parentHash: parentCastHash
|
|
2200
2378
|
}
|
|
2201
2379
|
});
|
|
2202
|
-
|
|
2203
|
-
return true;
|
|
2380
|
+
runtime.logger.info(`[REPLY_TO_CAST] Successfully replied to cast: ${reply.id}`);
|
|
2204
2381
|
} catch (error) {
|
|
2205
|
-
|
|
2206
|
-
|
|
2382
|
+
runtime.logger.error("[REPLY_TO_CAST] Error replying to cast:", typeof error === "string" ? error : error.message);
|
|
2383
|
+
throw error;
|
|
2207
2384
|
}
|
|
2208
2385
|
}
|
|
2209
2386
|
};
|
|
2210
2387
|
|
|
2211
2388
|
// src/actions/index.ts
|
|
2389
|
+
init_sendCast();
|
|
2212
2390
|
var farcasterActions = [sendCastAction, replyCastAction];
|
|
2213
2391
|
|
|
2214
2392
|
// src/providers/index.ts
|
|
2215
|
-
|
|
2393
|
+
init_profileProvider();
|
|
2394
|
+
init_timelineProvider();
|
|
2395
|
+
|
|
2396
|
+
// src/providers/threadProvider.ts
|
|
2397
|
+
init_constants();
|
|
2398
|
+
var formatTimestamp = (timestamp) => {
|
|
2399
|
+
return new Date(timestamp).toLocaleString("en-US", {
|
|
2400
|
+
hour: "2-digit",
|
|
2401
|
+
minute: "2-digit",
|
|
2402
|
+
month: "short",
|
|
2403
|
+
day: "numeric"
|
|
2404
|
+
});
|
|
2405
|
+
};
|
|
2406
|
+
var farcasterThreadProvider = {
|
|
2407
|
+
name: "farcasterThread",
|
|
2408
|
+
description: "Provides thread context for Farcaster casts so the agent can reference the full conversation.",
|
|
2409
|
+
get: async (runtime, message, _state) => {
|
|
2410
|
+
const source = message.content?.source;
|
|
2411
|
+
if (source !== FARCASTER_SOURCE) {
|
|
2412
|
+
return {
|
|
2413
|
+
text: "",
|
|
2414
|
+
data: {
|
|
2415
|
+
available: false,
|
|
2416
|
+
reason: `Not a Farcaster message (source=${source ?? "unknown"})`
|
|
2417
|
+
}
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2421
|
+
const messageService = service?.getMessageService(runtime.agentId);
|
|
2422
|
+
if (!messageService) {
|
|
2423
|
+
return {
|
|
2424
|
+
text: "Farcaster message service not available.",
|
|
2425
|
+
data: { available: false, error: "service_unavailable" }
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
const content = message.content;
|
|
2429
|
+
const messageMetadata = message.metadata ?? {};
|
|
2430
|
+
const castHash = content.hash || content.castHash || content?.metadata?.castHash || messageMetadata.castHash || content?.metadata?.parentHash || messageMetadata.parentHash;
|
|
2431
|
+
if (!castHash || typeof castHash !== "string") {
|
|
2432
|
+
return {
|
|
2433
|
+
text: "Unable to resolve Farcaster cast hash for this message.",
|
|
2434
|
+
data: { available: false, error: "missing_cast_hash" }
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
const threadMessages = await messageService.getThread({
|
|
2438
|
+
agentId: runtime.agentId,
|
|
2439
|
+
castHash
|
|
2440
|
+
});
|
|
2441
|
+
if (!threadMessages || threadMessages.length === 0) {
|
|
2442
|
+
runtime.logger.debug({ castHash }, "[FarcasterThreadProvider] No thread messages retrieved for cast.");
|
|
2443
|
+
return {
|
|
2444
|
+
text: "No Farcaster thread context available.",
|
|
2445
|
+
data: { available: false, castHash, count: 0 }
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
const formattedThread = threadMessages.map((msg, index) => {
|
|
2449
|
+
const time = formatTimestamp(msg.timestamp);
|
|
2450
|
+
const username = msg.username || msg.userId || "unknown";
|
|
2451
|
+
const marker = index === threadMessages.length - 1 ? "→" : "•";
|
|
2452
|
+
const text = msg.text && msg.text.trim().length > 0 ? msg.text : "<no text>";
|
|
2453
|
+
return `${marker} [${time}] @${username}: ${text}`;
|
|
2454
|
+
}).join(`
|
|
2455
|
+
`);
|
|
2456
|
+
return {
|
|
2457
|
+
text: `# Farcaster Thread Context
|
|
2458
|
+
${formattedThread}`,
|
|
2459
|
+
data: {
|
|
2460
|
+
available: true,
|
|
2461
|
+
castHash,
|
|
2462
|
+
count: threadMessages.length
|
|
2463
|
+
},
|
|
2464
|
+
values: {
|
|
2465
|
+
farcasterThread: formattedThread,
|
|
2466
|
+
farcasterCastHash: castHash,
|
|
2467
|
+
farcasterCurrentCastText: threadMessages[threadMessages.length - 1]?.text ?? "",
|
|
2468
|
+
farcasterParentCastText: threadMessages.length > 1 ? threadMessages[threadMessages.length - 2]?.text ?? "" : ""
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
|
|
2474
|
+
// src/providers/index.ts
|
|
2475
|
+
init_profileProvider();
|
|
2476
|
+
init_timelineProvider();
|
|
2477
|
+
var farcasterProviders = [
|
|
2478
|
+
farcasterProfileProvider,
|
|
2479
|
+
farcasterTimelineProvider,
|
|
2480
|
+
farcasterThreadProvider
|
|
2481
|
+
];
|
|
2482
|
+
|
|
2483
|
+
// src/routes/webhook.ts
|
|
2484
|
+
var farcasterWebhookRoutes = [
|
|
2485
|
+
{
|
|
2486
|
+
type: "POST",
|
|
2487
|
+
name: "Farcaster Webhook Handler",
|
|
2488
|
+
path: "/webhook",
|
|
2489
|
+
handler: async (req, res, runtime) => {
|
|
2490
|
+
try {
|
|
2491
|
+
const webhookData = req.body;
|
|
2492
|
+
const eventType = webhookData.type;
|
|
2493
|
+
const farcasterService = runtime?.getService?.("farcaster");
|
|
2494
|
+
if (farcasterService) {
|
|
2495
|
+
const agentManager = farcasterService.managers?.get?.(runtime.agentId);
|
|
2496
|
+
if (agentManager && agentManager.interactions) {
|
|
2497
|
+
if (agentManager.interactions.mode === "webhook") {
|
|
2498
|
+
console.log("Processing webhook through FarcasterInteractionManager...");
|
|
2499
|
+
await agentManager.interactions.processWebhookData(webhookData);
|
|
2500
|
+
} else {
|
|
2501
|
+
console.warn(`Agent is in ${agentManager.interactions.mode} mode, not webhook mode`);
|
|
2502
|
+
}
|
|
2503
|
+
} else {
|
|
2504
|
+
console.warn(`FarcasterAgentManager not found for agent ${runtime.agentId}`);
|
|
2505
|
+
}
|
|
2506
|
+
} else {
|
|
2507
|
+
console.warn("FarcasterService not found - webhook data logged only");
|
|
2508
|
+
}
|
|
2509
|
+
res.status(200).json({
|
|
2510
|
+
success: true,
|
|
2511
|
+
message: "Webhook processed successfully",
|
|
2512
|
+
event_type: eventType,
|
|
2513
|
+
timestamp: new Date().toISOString()
|
|
2514
|
+
});
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
console.error("Webhook processing error:", error);
|
|
2517
|
+
res.status(500).json({
|
|
2518
|
+
success: false,
|
|
2519
|
+
error: "Internal server error"
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
];
|
|
2216
2525
|
|
|
2217
2526
|
// src/index.ts
|
|
2218
2527
|
var farcasterPlugin = {
|
|
@@ -2221,10 +2530,12 @@ var farcasterPlugin = {
|
|
|
2221
2530
|
services: [FarcasterService],
|
|
2222
2531
|
actions: farcasterActions,
|
|
2223
2532
|
providers: farcasterProviders,
|
|
2224
|
-
|
|
2533
|
+
routes: farcasterWebhookRoutes,
|
|
2534
|
+
tests: [new FarcasterTestSuite]
|
|
2225
2535
|
};
|
|
2226
|
-
var
|
|
2536
|
+
var src_default = farcasterPlugin;
|
|
2227
2537
|
export {
|
|
2228
|
-
|
|
2538
|
+
src_default as default
|
|
2229
2539
|
};
|
|
2230
|
-
|
|
2540
|
+
|
|
2541
|
+
//# debugId=4C5340413D630C6A64756E2164756E21
|