@elizaos/plugin-farcaster 1.0.2 → 1.0.5
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 +162 -43
- package/dist/chunk-FNDASAYG.js +83 -0
- package/dist/chunk-FNDASAYG.js.map +1 -0
- package/dist/chunk-IOTLJXKN.js +72 -0
- package/dist/chunk-IOTLJXKN.js.map +1 -0
- package/dist/chunk-OAXQ6Z2Q.js +105 -0
- package/dist/chunk-OAXQ6Z2Q.js.map +1 -0
- package/dist/chunk-Y2URJ4EZ.js +21 -0
- package/dist/chunk-Y2URJ4EZ.js.map +1 -0
- package/dist/index.d.ts +415 -5
- package/dist/index.js +1159 -96
- package/dist/index.js.map +1 -1
- package/dist/profileProvider-TNRU42OO.js +8 -0
- package/dist/profileProvider-TNRU42OO.js.map +1 -0
- package/dist/sendCast-OW6DBKQB.js +8 -0
- package/dist/sendCast-OW6DBKQB.js.map +1 -0
- package/dist/timelineProvider-GPRPFEVJ.js +8 -0
- package/dist/timelineProvider-GPRPFEVJ.js.map +1 -0
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
sendCastAction
|
|
3
|
+
} from "./chunk-OAXQ6Z2Q.js";
|
|
4
|
+
import {
|
|
5
|
+
farcasterProfileProvider
|
|
6
|
+
} from "./chunk-IOTLJXKN.js";
|
|
7
|
+
import {
|
|
8
|
+
farcasterTimelineProvider
|
|
9
|
+
} from "./chunk-FNDASAYG.js";
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_CAST_CACHE_SIZE,
|
|
12
|
+
DEFAULT_CAST_CACHE_TTL,
|
|
13
|
+
DEFAULT_CAST_INTERVAL_MAX,
|
|
14
|
+
DEFAULT_CAST_INTERVAL_MIN,
|
|
15
|
+
DEFAULT_MAX_CAST_LENGTH,
|
|
16
|
+
DEFAULT_POLL_INTERVAL,
|
|
17
|
+
FARCASTER_SERVICE_NAME,
|
|
18
|
+
FARCASTER_SOURCE
|
|
19
|
+
} from "./chunk-Y2URJ4EZ.js";
|
|
3
20
|
|
|
4
|
-
// src/
|
|
5
|
-
|
|
6
|
-
var FARCASTER_SOURCE = "farcaster";
|
|
7
|
-
var DEFAULT_MAX_CAST_LENGTH = 320;
|
|
8
|
-
var DEFAULT_POLL_INTERVAL = 120;
|
|
9
|
-
var DEFAULT_POST_INTERVAL_MIN = 90;
|
|
10
|
-
var DEFAULT_POST_INTERVAL_MAX = 180;
|
|
11
|
-
var DEFAULT_CAST_CACHE_TTL = 1e3 * 30 * 60;
|
|
12
|
-
var DEFAULT_CAST_CACHE_SIZE = 9e3;
|
|
21
|
+
// src/service.ts
|
|
22
|
+
import { logger as logger8, Service } from "@elizaos/core";
|
|
13
23
|
|
|
14
24
|
// src/managers/agent.ts
|
|
15
25
|
import { logger as logger4 } from "@elizaos/core";
|
|
@@ -18,7 +28,7 @@ import { Configuration, NeynarAPIClient } from "@neynar/nodejs-sdk";
|
|
|
18
28
|
// src/client.ts
|
|
19
29
|
import { elizaLogger } from "@elizaos/core";
|
|
20
30
|
import { isApiErrorResponse } from "@neynar/nodejs-sdk";
|
|
21
|
-
import { CastParamType } from "@neynar/nodejs-sdk/build/api/
|
|
31
|
+
import { CastParamType } from "@neynar/nodejs-sdk/build/api/index.js";
|
|
22
32
|
import { LRUCache } from "lru-cache";
|
|
23
33
|
|
|
24
34
|
// src/common/utils.ts
|
|
@@ -33,30 +43,30 @@ function castUuid(props) {
|
|
|
33
43
|
function splitPostContent(content, maxLength = MAX_CAST_LENGTH) {
|
|
34
44
|
const paragraphs = content.split("\n\n").map((p) => p.trim());
|
|
35
45
|
const posts = [];
|
|
36
|
-
let
|
|
46
|
+
let currentCast = "";
|
|
37
47
|
for (const paragraph of paragraphs) {
|
|
38
48
|
if (!paragraph) continue;
|
|
39
|
-
if ((
|
|
40
|
-
if (
|
|
41
|
-
|
|
49
|
+
if ((currentCast + "\n\n" + paragraph).trim().length <= maxLength) {
|
|
50
|
+
if (currentCast) {
|
|
51
|
+
currentCast += "\n\n" + paragraph;
|
|
42
52
|
} else {
|
|
43
|
-
|
|
53
|
+
currentCast = paragraph;
|
|
44
54
|
}
|
|
45
55
|
} else {
|
|
46
|
-
if (
|
|
47
|
-
posts.push(
|
|
56
|
+
if (currentCast) {
|
|
57
|
+
posts.push(currentCast.trim());
|
|
48
58
|
}
|
|
49
59
|
if (paragraph.length <= maxLength) {
|
|
50
|
-
|
|
60
|
+
currentCast = paragraph;
|
|
51
61
|
} else {
|
|
52
62
|
const chunks = splitParagraph(paragraph, maxLength);
|
|
53
63
|
posts.push(...chunks.slice(0, -1));
|
|
54
|
-
|
|
64
|
+
currentCast = chunks[chunks.length - 1];
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
|
-
if (
|
|
59
|
-
posts.push(
|
|
68
|
+
if (currentCast) {
|
|
69
|
+
posts.push(currentCast.trim());
|
|
60
70
|
}
|
|
61
71
|
return posts;
|
|
62
72
|
}
|
|
@@ -106,6 +116,7 @@ function lastCastCacheKey(fid) {
|
|
|
106
116
|
return `farcaster/${fid}/lastCast`;
|
|
107
117
|
}
|
|
108
118
|
function neynarCastToCast(neynarCast) {
|
|
119
|
+
var _a;
|
|
109
120
|
return {
|
|
110
121
|
hash: neynarCast.hash,
|
|
111
122
|
authorFid: neynarCast.author.fid,
|
|
@@ -116,7 +127,7 @@ function neynarCastToCast(neynarCast) {
|
|
|
116
127
|
name: neynarCast.author.display_name || "anon",
|
|
117
128
|
username: neynarCast.author.username
|
|
118
129
|
},
|
|
119
|
-
...neynarCast.parent_hash ? {
|
|
130
|
+
...neynarCast.parent_hash && ((_a = neynarCast.parent_author) == null ? void 0 : _a.fid) ? {
|
|
120
131
|
inReplyTo: {
|
|
121
132
|
hash: neynarCast.parent_hash,
|
|
122
133
|
fid: neynarCast.parent_author.fid
|
|
@@ -436,14 +447,14 @@ var FarcasterConfigSchema = z.object({
|
|
|
436
447
|
FARCASTER_FID: z.number().int().min(1, "Farcaster fid is required"),
|
|
437
448
|
MAX_CAST_LENGTH: z.number().int().default(DEFAULT_MAX_CAST_LENGTH),
|
|
438
449
|
FARCASTER_POLL_INTERVAL: z.number().int().default(DEFAULT_POLL_INTERVAL),
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
450
|
+
ENABLE_CAST: z.union([z.boolean(), z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
451
|
+
CAST_INTERVAL_MIN: z.number().int(),
|
|
452
|
+
CAST_INTERVAL_MAX: z.number().int(),
|
|
442
453
|
ENABLE_ACTION_PROCESSING: z.union([z.boolean(), z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
443
454
|
ACTION_INTERVAL: z.number().int(),
|
|
444
|
-
|
|
455
|
+
CAST_IMMEDIATELY: z.union([z.boolean(), z.string()]).transform((val) => typeof val === "string" ? val.toLowerCase() === "true" : val),
|
|
445
456
|
MAX_ACTIONS_PROCESSING: z.number().int(),
|
|
446
|
-
|
|
457
|
+
FARCASTER_SIGNER_UUID: z.string().min(1, "FARCASTER_SIGNER_UUID is not set"),
|
|
447
458
|
FARCASTER_NEYNAR_API_KEY: z.string().min(1, "FARCASTER_NEYNAR_API_KEY is not set"),
|
|
448
459
|
FARCASTER_HUB_URL: z.string().min(1, "FARCASTER_HUB_URL is not set")
|
|
449
460
|
});
|
|
@@ -676,7 +687,7 @@ var FarcasterInteractionManager = class {
|
|
|
676
687
|
|
|
677
688
|
// src/managers/post.ts
|
|
678
689
|
import { createUniqueUuid as createUniqueUuid2, EventType as EventType2, logger as logger3 } from "@elizaos/core";
|
|
679
|
-
var
|
|
690
|
+
var FarcasterCastManager = class {
|
|
680
691
|
client;
|
|
681
692
|
runtime;
|
|
682
693
|
fid;
|
|
@@ -690,7 +701,7 @@ var FarcasterPostManager = class {
|
|
|
690
701
|
this.fid = this.config.FARCASTER_FID;
|
|
691
702
|
}
|
|
692
703
|
async start() {
|
|
693
|
-
if (this.isRunning || !this.config.
|
|
704
|
+
if (this.isRunning || !this.config.ENABLE_CAST) {
|
|
694
705
|
return;
|
|
695
706
|
}
|
|
696
707
|
this.isRunning = true;
|
|
@@ -701,14 +712,14 @@ var FarcasterPostManager = class {
|
|
|
701
712
|
this.isRunning = false;
|
|
702
713
|
}
|
|
703
714
|
calculateDelay() {
|
|
704
|
-
const minMinutes = this.config.
|
|
705
|
-
const maxMinutes = this.config.
|
|
715
|
+
const minMinutes = this.config.CAST_INTERVAL_MIN;
|
|
716
|
+
const maxMinutes = this.config.CAST_INTERVAL_MAX;
|
|
706
717
|
const randomMinutes = Math.floor(Math.random() * (maxMinutes - minMinutes + 1)) + minMinutes;
|
|
707
718
|
const delay = randomMinutes * 60 * 1e3;
|
|
708
719
|
return { delay, randomMinutes };
|
|
709
720
|
}
|
|
710
721
|
async runPeriodically() {
|
|
711
|
-
if (this.config.
|
|
722
|
+
if (this.config.CAST_IMMEDIATELY) {
|
|
712
723
|
await this.generateNewCast();
|
|
713
724
|
}
|
|
714
725
|
while (this.isRunning) {
|
|
@@ -722,7 +733,7 @@ var FarcasterPostManager = class {
|
|
|
722
733
|
logger3.log(`Next cast scheduled in ${randomMinutes} minutes`);
|
|
723
734
|
await new Promise((resolve) => this.timeout = setTimeout(resolve, delay));
|
|
724
735
|
} catch (error) {
|
|
725
|
-
logger3.error("[Farcaster] Error in periodic
|
|
736
|
+
logger3.error("[Farcaster] Error in periodic cast loop:", this.runtime.agentId, error);
|
|
726
737
|
}
|
|
727
738
|
}
|
|
728
739
|
}
|
|
@@ -744,7 +755,7 @@ var FarcasterPostManager = class {
|
|
|
744
755
|
});
|
|
745
756
|
}
|
|
746
757
|
});
|
|
747
|
-
this.runtime.emitEvent([EventType2.POST_GENERATED, "
|
|
758
|
+
this.runtime.emitEvent([EventType2.POST_GENERATED, "FARCASTER_CAST_GENERATED" /* CAST_GENERATED */], {
|
|
748
759
|
runtime: this.runtime,
|
|
749
760
|
callback,
|
|
750
761
|
worldId,
|
|
@@ -762,24 +773,24 @@ var FarcasterPostManager = class {
|
|
|
762
773
|
var FarcasterAgentManager = class {
|
|
763
774
|
runtime;
|
|
764
775
|
client;
|
|
765
|
-
|
|
776
|
+
casts;
|
|
766
777
|
interactions;
|
|
767
778
|
constructor(runtime, config) {
|
|
768
779
|
this.runtime = runtime;
|
|
769
|
-
const signerUuid = config.
|
|
780
|
+
const signerUuid = config.FARCASTER_SIGNER_UUID;
|
|
770
781
|
const neynarConfig = new Configuration({ apiKey: config.FARCASTER_NEYNAR_API_KEY });
|
|
771
782
|
const neynar = new NeynarAPIClient(neynarConfig);
|
|
772
783
|
const client = new FarcasterClient({ neynar, signerUuid });
|
|
773
784
|
this.client = client;
|
|
774
785
|
logger4.success("Farcaster Neynar client initialized.");
|
|
775
|
-
this.
|
|
786
|
+
this.casts = new FarcasterCastManager({ client, runtime, config });
|
|
776
787
|
this.interactions = new FarcasterInteractionManager({ client, runtime, config });
|
|
777
788
|
}
|
|
778
789
|
async start() {
|
|
779
|
-
await Promise.all([this.
|
|
790
|
+
await Promise.all([this.casts.start(), this.interactions.start()]);
|
|
780
791
|
}
|
|
781
792
|
async stop() {
|
|
782
|
-
await Promise.all([this.
|
|
793
|
+
await Promise.all([this.casts.stop(), this.interactions.stop()]);
|
|
783
794
|
}
|
|
784
795
|
};
|
|
785
796
|
|
|
@@ -793,7 +804,7 @@ function safeParseInt(value, defaultValue) {
|
|
|
793
804
|
}
|
|
794
805
|
function hasFarcasterEnabled(runtime) {
|
|
795
806
|
const fid = runtime.getSetting("FARCASTER_FID") || process.env.FARCASTER_FID;
|
|
796
|
-
const neynarSignerUuid = runtime.getSetting("
|
|
807
|
+
const neynarSignerUuid = runtime.getSetting("FARCASTER_SIGNER_UUID") || process.env.FARCASTER_SIGNER_UUID;
|
|
797
808
|
const neynarApiKey = runtime.getSetting("FARCASTER_NEYNAR_API_KEY") || process.env.FARCASTER_NEYNAR_API_KEY;
|
|
798
809
|
return fid && neynarSignerUuid && neynarApiKey;
|
|
799
810
|
}
|
|
@@ -811,14 +822,14 @@ function validateFarcasterConfig(runtime) {
|
|
|
811
822
|
runtime.getSetting("FARCASTER_POLL_INTERVAL") || process.env.FARCASTER_POLL_INTERVAL,
|
|
812
823
|
DEFAULT_POLL_INTERVAL
|
|
813
824
|
),
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
runtime.getSetting("
|
|
817
|
-
|
|
825
|
+
ENABLE_CAST: runtime.getSetting("ENABLE_CAST") || parseBooleanFromText(process.env.ENABLE_CAST || "true"),
|
|
826
|
+
CAST_INTERVAL_MIN: safeParseInt(
|
|
827
|
+
runtime.getSetting("CAST_INTERVAL_MIN") || process.env.CAST_INTERVAL_MIN,
|
|
828
|
+
DEFAULT_CAST_INTERVAL_MIN
|
|
818
829
|
),
|
|
819
|
-
|
|
820
|
-
runtime.getSetting("
|
|
821
|
-
|
|
830
|
+
CAST_INTERVAL_MAX: safeParseInt(
|
|
831
|
+
runtime.getSetting("CAST_INTERVAL_MAX") || process.env.CAST_INTERVAL_MAX,
|
|
832
|
+
DEFAULT_CAST_INTERVAL_MAX
|
|
822
833
|
),
|
|
823
834
|
ENABLE_ACTION_PROCESSING: runtime.getSetting("ENABLE_ACTION_PROCESSING") || parseBooleanFromText(process.env.ENABLE_ACTION_PROCESSING || "false"),
|
|
824
835
|
ACTION_INTERVAL: safeParseInt(
|
|
@@ -826,12 +837,12 @@ function validateFarcasterConfig(runtime) {
|
|
|
826
837
|
5
|
|
827
838
|
),
|
|
828
839
|
// 5 minutes
|
|
829
|
-
|
|
840
|
+
CAST_IMMEDIATELY: runtime.getSetting("CAST_IMMEDIATELY") || parseBooleanFromText(process.env.CAST_IMMEDIATELY || "false"),
|
|
830
841
|
MAX_ACTIONS_PROCESSING: safeParseInt(
|
|
831
842
|
runtime.getSetting("MAX_ACTIONS_PROCESSING") || process.env.MAX_ACTIONS_PROCESSING,
|
|
832
843
|
1
|
|
833
844
|
),
|
|
834
|
-
|
|
845
|
+
FARCASTER_SIGNER_UUID: runtime.getSetting("FARCASTER_SIGNER_UUID") || process.env.FARCASTER_SIGNER_UUID,
|
|
835
846
|
FARCASTER_NEYNAR_API_KEY: runtime.getSetting("FARCASTER_NEYNAR_API_KEY") || process.env.FARCASTER_NEYNAR_API_KEY,
|
|
836
847
|
FARCASTER_HUB_URL: runtime.getSetting("FARCASTER_HUB_URL") || process.env.FARCASTER_HUB_URL || "hub.pinata.cloud"
|
|
837
848
|
};
|
|
@@ -840,12 +851,12 @@ function validateFarcasterConfig(runtime) {
|
|
|
840
851
|
logger5.log("Farcaster Client Configuration:");
|
|
841
852
|
logger5.log(`- FID: ${config.FARCASTER_FID}`);
|
|
842
853
|
logger5.log(`- Dry Run Mode: ${isDryRun ? "enabled" : "disabled"}`);
|
|
843
|
-
logger5.log(`- Enable
|
|
844
|
-
if (config.
|
|
854
|
+
logger5.log(`- Enable Cast: ${config.ENABLE_CAST ? "enabled" : "disabled"}`);
|
|
855
|
+
if (config.ENABLE_CAST) {
|
|
845
856
|
logger5.log(
|
|
846
|
-
`-
|
|
857
|
+
`- Cast Interval: ${config.CAST_INTERVAL_MIN}-${config.CAST_INTERVAL_MAX} minutes`
|
|
847
858
|
);
|
|
848
|
-
logger5.log(`-
|
|
859
|
+
logger5.log(`- Cast Immediately: ${config.CAST_IMMEDIATELY ? "enabled" : "disabled"}`);
|
|
849
860
|
}
|
|
850
861
|
logger5.log(`- Action Processing: ${config.ENABLE_ACTION_PROCESSING ? "enabled" : "disabled"}`);
|
|
851
862
|
logger5.log(`- Action Interval: ${config.ACTION_INTERVAL} minutes`);
|
|
@@ -863,11 +874,389 @@ ${errorMessages}`);
|
|
|
863
874
|
}
|
|
864
875
|
}
|
|
865
876
|
|
|
877
|
+
// src/services/MessageService.ts
|
|
878
|
+
import {
|
|
879
|
+
logger as logger6,
|
|
880
|
+
createUniqueUuid as createUniqueUuid3
|
|
881
|
+
} from "@elizaos/core";
|
|
882
|
+
var FarcasterMessageService = class {
|
|
883
|
+
constructor(client, runtime) {
|
|
884
|
+
this.client = client;
|
|
885
|
+
this.runtime = runtime;
|
|
886
|
+
}
|
|
887
|
+
async getMessages(options) {
|
|
888
|
+
try {
|
|
889
|
+
const { roomId, limit = 20 } = options;
|
|
890
|
+
const { timeline } = await this.client.getTimeline({
|
|
891
|
+
fid: parseInt(
|
|
892
|
+
this.runtime.getSetting("FARCASTER_FID") || this.runtime.config.FARCASTER_FID
|
|
893
|
+
),
|
|
894
|
+
pageSize: limit
|
|
895
|
+
});
|
|
896
|
+
const messages = timeline.filter((cast) => {
|
|
897
|
+
if (roomId) {
|
|
898
|
+
const castRoomId = createUniqueUuid3(this.runtime, cast.threadId || cast.hash);
|
|
899
|
+
return castRoomId === roomId;
|
|
900
|
+
}
|
|
901
|
+
return true;
|
|
902
|
+
}).map((cast) => ({
|
|
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
|
+
}));
|
|
918
|
+
return messages;
|
|
919
|
+
} catch (error) {
|
|
920
|
+
logger6.error("[Farcaster] Error fetching messages:", error);
|
|
921
|
+
return [];
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
async sendMessage(options) {
|
|
925
|
+
var _a;
|
|
926
|
+
try {
|
|
927
|
+
const { text, type, roomId, replyToId, agentId } = options;
|
|
928
|
+
let inReplyTo = void 0;
|
|
929
|
+
if (replyToId && type === "REPLY" /* REPLY */) {
|
|
930
|
+
const parentHash = ((_a = options.metadata) == null ? void 0 : _a.parentHash) || replyToId;
|
|
931
|
+
inReplyTo = {
|
|
932
|
+
hash: parentHash,
|
|
933
|
+
fid: parseInt(
|
|
934
|
+
this.runtime.getSetting("FARCASTER_FID") || this.runtime.config.FARCASTER_FID
|
|
935
|
+
)
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
const casts = await this.client.sendCast({
|
|
939
|
+
content: { text },
|
|
940
|
+
inReplyTo
|
|
941
|
+
});
|
|
942
|
+
if (casts.length === 0) {
|
|
943
|
+
throw new Error("No cast was created");
|
|
944
|
+
}
|
|
945
|
+
const cast = neynarCastToCast(casts[0]);
|
|
946
|
+
const message = {
|
|
947
|
+
id: castUuid({ hash: cast.hash, agentId }),
|
|
948
|
+
agentId,
|
|
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
|
+
};
|
|
963
|
+
await this.runtime.emitEvent("FARCASTER_CAST_GENERATED" /* CAST_GENERATED */, {
|
|
964
|
+
runtime: this.runtime,
|
|
965
|
+
castHash: cast.hash,
|
|
966
|
+
message,
|
|
967
|
+
threadId: cast.threadId
|
|
968
|
+
});
|
|
969
|
+
return message;
|
|
970
|
+
} catch (error) {
|
|
971
|
+
logger6.error("[Farcaster] Error sending message:", error);
|
|
972
|
+
throw error;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async deleteMessage(messageId, agentId) {
|
|
976
|
+
logger6.warn("[Farcaster] Cast deletion is not supported by the Farcaster API");
|
|
977
|
+
}
|
|
978
|
+
async getMessage(messageId, agentId) {
|
|
979
|
+
try {
|
|
980
|
+
const castHash = messageId;
|
|
981
|
+
const cast = await this.client.getCast(castHash);
|
|
982
|
+
const farcasterCast = neynarCastToCast(cast);
|
|
983
|
+
const message = {
|
|
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;
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
logger6.error("[Farcaster] Error fetching message:", error);
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
async markAsRead(messageIds, agentId) {
|
|
1006
|
+
logger6.debug("[Farcaster] Mark as read is not applicable for Farcaster casts");
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// src/services/CastService.ts
|
|
1011
|
+
import {
|
|
1012
|
+
logger as logger7,
|
|
1013
|
+
ModelType as ModelType2,
|
|
1014
|
+
createUniqueUuid as createUniqueUuid4
|
|
1015
|
+
} from "@elizaos/core";
|
|
1016
|
+
var FarcasterCastService = class {
|
|
1017
|
+
constructor(client, runtime) {
|
|
1018
|
+
this.client = client;
|
|
1019
|
+
this.runtime = runtime;
|
|
1020
|
+
}
|
|
1021
|
+
static serviceType = "ICastService";
|
|
1022
|
+
/**
|
|
1023
|
+
* Get recent casts from the timeline
|
|
1024
|
+
*/
|
|
1025
|
+
async getCasts(params) {
|
|
1026
|
+
var _a, _b;
|
|
1027
|
+
try {
|
|
1028
|
+
const { timeline } = await this.client.getTimeline({
|
|
1029
|
+
fid: ((_a = this.runtime.config) == null ? void 0 : _a.FARCASTER_FID) || ((_b = this.runtime.settings) == null ? void 0 : _b.FARCASTER_FID),
|
|
1030
|
+
pageSize: params.limit || 50
|
|
1031
|
+
});
|
|
1032
|
+
return timeline.map((cast) => this.castToFarcasterCast(cast, params.agentId));
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
logger7.error("Failed to get casts", { params, error });
|
|
1035
|
+
return [];
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Create a new cast
|
|
1040
|
+
*/
|
|
1041
|
+
async createCast(params) {
|
|
1042
|
+
var _a;
|
|
1043
|
+
try {
|
|
1044
|
+
let castText = params.text;
|
|
1045
|
+
if (!castText || castText.trim() === "") {
|
|
1046
|
+
castText = await this.generateCastContent();
|
|
1047
|
+
}
|
|
1048
|
+
if (castText.length > 320) {
|
|
1049
|
+
castText = await this.truncateCast(castText);
|
|
1050
|
+
}
|
|
1051
|
+
const casts = await this.client.sendCast({
|
|
1052
|
+
content: { text: castText },
|
|
1053
|
+
inReplyTo: params.replyTo ? { hash: params.replyTo.hash, fid: params.replyTo.fid } : void 0
|
|
1054
|
+
});
|
|
1055
|
+
if (casts.length === 0) {
|
|
1056
|
+
throw new Error("No cast was created");
|
|
1057
|
+
}
|
|
1058
|
+
const cast = neynarCastToCast(casts[0]);
|
|
1059
|
+
const farcasterCast = {
|
|
1060
|
+
id: castUuid({ hash: cast.hash, agentId: params.agentId }),
|
|
1061
|
+
agentId: params.agentId,
|
|
1062
|
+
roomId: params.roomId,
|
|
1063
|
+
userId: cast.profile.fid.toString(),
|
|
1064
|
+
username: cast.profile.username,
|
|
1065
|
+
text: cast.text,
|
|
1066
|
+
timestamp: cast.timestamp.getTime(),
|
|
1067
|
+
inReplyTo: (_a = params.replyTo) == null ? void 0 : _a.hash,
|
|
1068
|
+
media: [],
|
|
1069
|
+
// TODO: Handle media upload when Farcaster API supports it
|
|
1070
|
+
metadata: {
|
|
1071
|
+
castHash: cast.hash,
|
|
1072
|
+
threadId: cast.threadId,
|
|
1073
|
+
authorFid: cast.authorFid,
|
|
1074
|
+
source: FARCASTER_SOURCE
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
await this.storeCastInMemory(params.roomId, farcasterCast);
|
|
1078
|
+
return farcasterCast;
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
logger7.error("Failed to create cast", { params, error });
|
|
1081
|
+
throw error;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Delete a cast
|
|
1086
|
+
*/
|
|
1087
|
+
async deleteCast(params) {
|
|
1088
|
+
try {
|
|
1089
|
+
logger7.warn("Cast deletion is not supported by the Farcaster API", { castHash: params.castHash });
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
logger7.error("Failed to delete cast", { params, error });
|
|
1092
|
+
throw error;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Like a cast
|
|
1097
|
+
*/
|
|
1098
|
+
async likeCast(params) {
|
|
1099
|
+
try {
|
|
1100
|
+
logger7.info("Like functionality not yet implemented for cast", { castHash: params.castHash });
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
logger7.error("Failed to like cast", { params, error });
|
|
1103
|
+
throw error;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Unlike a cast
|
|
1108
|
+
*/
|
|
1109
|
+
async unlikeCast(params) {
|
|
1110
|
+
try {
|
|
1111
|
+
logger7.info("Unlike functionality not yet implemented for cast", { castHash: params.castHash });
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
logger7.error("Failed to unlike cast", { params, error });
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Recast a cast
|
|
1119
|
+
*/
|
|
1120
|
+
async recast(params) {
|
|
1121
|
+
try {
|
|
1122
|
+
logger7.info("Recast functionality not yet implemented for cast", { castHash: params.castHash });
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
logger7.error("Failed to recast", { params, error });
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Remove a recast
|
|
1130
|
+
*/
|
|
1131
|
+
async unrecast(params) {
|
|
1132
|
+
try {
|
|
1133
|
+
logger7.info("Remove recast functionality not yet implemented for cast", { castHash: params.castHash });
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
logger7.error("Failed to remove recast", { params, error });
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Get mentions
|
|
1141
|
+
*/
|
|
1142
|
+
async getMentions(params) {
|
|
1143
|
+
var _a, _b;
|
|
1144
|
+
try {
|
|
1145
|
+
const mentions = await this.client.getMentions({
|
|
1146
|
+
fid: ((_a = this.runtime.config) == null ? void 0 : _a.FARCASTER_FID) || ((_b = this.runtime.settings) == null ? void 0 : _b.FARCASTER_FID),
|
|
1147
|
+
pageSize: params.limit || 20
|
|
1148
|
+
});
|
|
1149
|
+
return mentions.map((castWithInteractions) => {
|
|
1150
|
+
const cast = neynarCastToCast(castWithInteractions);
|
|
1151
|
+
return this.castToFarcasterCast(cast, params.agentId);
|
|
1152
|
+
});
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
logger7.error("Failed to get mentions", { params, error });
|
|
1155
|
+
return [];
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Generate cast content using AI
|
|
1160
|
+
*/
|
|
1161
|
+
async generateCastContent() {
|
|
1162
|
+
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
|
+
try {
|
|
1164
|
+
const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
1165
|
+
prompt,
|
|
1166
|
+
maxTokens: 100
|
|
1167
|
+
});
|
|
1168
|
+
return response;
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
logger7.error("Failed to generate cast content", { error });
|
|
1171
|
+
return "Hello Farcaster! \u{1F44B}";
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Truncate cast to fit character limit
|
|
1176
|
+
*/
|
|
1177
|
+
async truncateCast(text) {
|
|
1178
|
+
const prompt = `Shorten this text to under 320 characters while keeping the main message intact: "${text}"`;
|
|
1179
|
+
try {
|
|
1180
|
+
const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
|
|
1181
|
+
prompt,
|
|
1182
|
+
maxTokens: 100
|
|
1183
|
+
});
|
|
1184
|
+
const truncated = response;
|
|
1185
|
+
if (truncated.length > 320) {
|
|
1186
|
+
return truncated.substring(0, 317) + "...";
|
|
1187
|
+
}
|
|
1188
|
+
return truncated;
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
logger7.error("Failed to truncate cast", { error });
|
|
1191
|
+
return text.substring(0, 317) + "...";
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Store cast in agent memory
|
|
1196
|
+
*/
|
|
1197
|
+
async storeCastInMemory(roomId, cast) {
|
|
1198
|
+
var _a;
|
|
1199
|
+
try {
|
|
1200
|
+
const memory = {
|
|
1201
|
+
id: createUniqueUuid4(this.runtime, cast.id),
|
|
1202
|
+
agentId: this.runtime.agentId,
|
|
1203
|
+
content: {
|
|
1204
|
+
text: cast.text,
|
|
1205
|
+
castHash: (_a = cast.metadata) == null ? void 0 : _a.castHash,
|
|
1206
|
+
castId: cast.id,
|
|
1207
|
+
author: cast.username,
|
|
1208
|
+
timestamp: cast.timestamp
|
|
1209
|
+
},
|
|
1210
|
+
roomId,
|
|
1211
|
+
userId: cast.userId,
|
|
1212
|
+
createdAt: Date.now()
|
|
1213
|
+
};
|
|
1214
|
+
if (typeof this.runtime.storeMemory === "function") {
|
|
1215
|
+
await this.runtime.storeMemory(memory);
|
|
1216
|
+
} else if (this.runtime.memory && typeof this.runtime.memory.create === "function") {
|
|
1217
|
+
await this.runtime.memory.create(memory);
|
|
1218
|
+
} else {
|
|
1219
|
+
logger7.warn("Memory storage method not available in runtime");
|
|
1220
|
+
}
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
logger7.error("Failed to store cast in memory", { error });
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Convert internal Cast type to FarcasterCast
|
|
1227
|
+
*/
|
|
1228
|
+
castToFarcasterCast(cast, agentId) {
|
|
1229
|
+
return {
|
|
1230
|
+
id: castUuid({ hash: cast.hash, agentId }),
|
|
1231
|
+
agentId,
|
|
1232
|
+
roomId: createUniqueUuid4(this.runtime, cast.threadId || cast.hash),
|
|
1233
|
+
userId: cast.profile.fid.toString(),
|
|
1234
|
+
username: cast.profile.username,
|
|
1235
|
+
text: cast.text,
|
|
1236
|
+
timestamp: cast.timestamp.getTime(),
|
|
1237
|
+
media: [],
|
|
1238
|
+
// Farcaster casts can have embedded media but not in our Cast type
|
|
1239
|
+
metadata: {
|
|
1240
|
+
castHash: cast.hash,
|
|
1241
|
+
threadId: cast.threadId,
|
|
1242
|
+
authorFid: cast.authorFid,
|
|
1243
|
+
source: FARCASTER_SOURCE,
|
|
1244
|
+
stats: cast.stats
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
|
|
866
1250
|
// src/service.ts
|
|
867
1251
|
var FarcasterService = class _FarcasterService extends Service {
|
|
868
1252
|
static instance;
|
|
869
1253
|
managers = /* @__PURE__ */ new Map();
|
|
1254
|
+
messageServices = /* @__PURE__ */ new Map();
|
|
1255
|
+
castServices = /* @__PURE__ */ new Map();
|
|
1256
|
+
// Properly implement serviceType for discoverability
|
|
870
1257
|
static serviceType = FARCASTER_SERVICE_NAME;
|
|
1258
|
+
// Add service description
|
|
1259
|
+
description = "Farcaster integration service for sending and receiving casts";
|
|
871
1260
|
capabilityDescription = "The agent is able to send and receive messages on farcaster";
|
|
872
1261
|
static getInstance() {
|
|
873
1262
|
if (!_FarcasterService.instance) {
|
|
@@ -875,23 +1264,31 @@ var FarcasterService = class _FarcasterService extends Service {
|
|
|
875
1264
|
}
|
|
876
1265
|
return _FarcasterService.instance;
|
|
877
1266
|
}
|
|
1267
|
+
// Required by ElizaOS Service base class
|
|
1268
|
+
async initialize(runtime) {
|
|
1269
|
+
await _FarcasterService.start(runtime);
|
|
1270
|
+
}
|
|
878
1271
|
// Called to start a single Farcaster service
|
|
879
1272
|
static async start(runtime) {
|
|
880
1273
|
const service = _FarcasterService.getInstance();
|
|
881
1274
|
let manager = service.managers.get(runtime.agentId);
|
|
882
1275
|
if (manager) {
|
|
883
|
-
|
|
1276
|
+
logger8.warn("Farcaster service already started", runtime.agentId);
|
|
884
1277
|
return service;
|
|
885
1278
|
}
|
|
886
1279
|
if (!hasFarcasterEnabled(runtime)) {
|
|
887
|
-
|
|
1280
|
+
logger8.debug("Farcaster service not enabled", runtime.agentId);
|
|
888
1281
|
return service;
|
|
889
1282
|
}
|
|
890
1283
|
const farcasterConfig = validateFarcasterConfig(runtime);
|
|
891
1284
|
manager = new FarcasterAgentManager(runtime, farcasterConfig);
|
|
892
1285
|
service.managers.set(runtime.agentId, manager);
|
|
1286
|
+
const messageService = new FarcasterMessageService(manager.client, runtime);
|
|
1287
|
+
const castService = new FarcasterCastService(manager.client, runtime);
|
|
1288
|
+
service.messageServices.set(runtime.agentId, messageService);
|
|
1289
|
+
service.castServices.set(runtime.agentId, castService);
|
|
893
1290
|
await manager.start();
|
|
894
|
-
|
|
1291
|
+
logger8.success("Farcaster client started", runtime.agentId);
|
|
895
1292
|
return service;
|
|
896
1293
|
}
|
|
897
1294
|
// Called to stop a single Farcaster service
|
|
@@ -901,33 +1298,84 @@ var FarcasterService = class _FarcasterService extends Service {
|
|
|
901
1298
|
if (manager) {
|
|
902
1299
|
await manager.stop();
|
|
903
1300
|
service.managers.delete(runtime.agentId);
|
|
904
|
-
|
|
1301
|
+
service.messageServices.delete(runtime.agentId);
|
|
1302
|
+
service.castServices.delete(runtime.agentId);
|
|
1303
|
+
logger8.info("Farcaster client stopped", runtime.agentId);
|
|
905
1304
|
} else {
|
|
906
|
-
|
|
1305
|
+
logger8.debug("Farcaster service not running", runtime.agentId);
|
|
907
1306
|
}
|
|
908
1307
|
}
|
|
909
1308
|
// Called to stop all Farcaster services
|
|
910
1309
|
async stop() {
|
|
911
|
-
|
|
1310
|
+
logger8.debug("Stopping ALL Farcaster services");
|
|
912
1311
|
for (const manager of Array.from(this.managers.values())) {
|
|
913
1312
|
const agentId = manager.runtime.agentId;
|
|
914
1313
|
try {
|
|
915
1314
|
await _FarcasterService.stop(manager.runtime);
|
|
916
1315
|
} catch (error) {
|
|
917
|
-
|
|
1316
|
+
logger8.error("Error stopping Farcaster service", agentId, error);
|
|
918
1317
|
}
|
|
919
1318
|
}
|
|
920
1319
|
}
|
|
1320
|
+
// Get the MessageService for a specific agent
|
|
1321
|
+
getMessageService(agentId) {
|
|
1322
|
+
return this.messageServices.get(agentId);
|
|
1323
|
+
}
|
|
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
|
+
getPostService(agentId) {
|
|
1329
|
+
return this.castServices.get(agentId);
|
|
1330
|
+
}
|
|
1331
|
+
// Get the CastService for a specific agent
|
|
1332
|
+
getCastService(agentId) {
|
|
1333
|
+
return this.castServices.get(agentId);
|
|
1334
|
+
}
|
|
1335
|
+
// Add health check method
|
|
1336
|
+
async healthCheck() {
|
|
1337
|
+
const managerStatuses = {};
|
|
1338
|
+
let overallHealthy = true;
|
|
1339
|
+
for (const [agentId, manager] of Array.from(this.managers.entries())) {
|
|
1340
|
+
try {
|
|
1341
|
+
const profile = await manager.client.getProfile(
|
|
1342
|
+
parseInt(manager.runtime.getSetting("FARCASTER_FID"))
|
|
1343
|
+
);
|
|
1344
|
+
managerStatuses[agentId] = {
|
|
1345
|
+
status: "healthy",
|
|
1346
|
+
fid: profile.fid,
|
|
1347
|
+
username: profile.username
|
|
1348
|
+
};
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
managerStatuses[agentId] = {
|
|
1351
|
+
status: "unhealthy",
|
|
1352
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1353
|
+
};
|
|
1354
|
+
overallHealthy = false;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
return {
|
|
1358
|
+
healthy: overallHealthy,
|
|
1359
|
+
details: {
|
|
1360
|
+
activeManagers: this.managers.size,
|
|
1361
|
+
managerStatuses
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
// Get all active managers (for monitoring)
|
|
1366
|
+
getActiveManagers() {
|
|
1367
|
+
return new Map(this.managers);
|
|
1368
|
+
}
|
|
921
1369
|
};
|
|
922
1370
|
|
|
923
|
-
// __tests__/suite.ts
|
|
1371
|
+
// src/__tests__/suite.ts
|
|
924
1372
|
import {
|
|
925
|
-
ModelType as
|
|
926
|
-
createUniqueUuid as
|
|
927
|
-
logger as
|
|
1373
|
+
ModelType as ModelType3,
|
|
1374
|
+
createUniqueUuid as createUniqueUuid6,
|
|
1375
|
+
logger as logger10
|
|
928
1376
|
} from "@elizaos/core";
|
|
929
1377
|
|
|
930
|
-
// __tests__/test-utils.ts
|
|
1378
|
+
// src/__tests__/test-utils.ts
|
|
931
1379
|
var TEST_IMAGE_URL = "https://github.com/elizaOS/awesome-eliza/blob/main/assets/eliza-logo.jpg?raw=true";
|
|
932
1380
|
var TEST_IMAGE = {
|
|
933
1381
|
id: "mock-image-id",
|
|
@@ -940,9 +1388,276 @@ var TEST_IMAGE = {
|
|
|
940
1388
|
alt_text: "mock image"
|
|
941
1389
|
};
|
|
942
1390
|
|
|
943
|
-
// __tests__/
|
|
1391
|
+
// src/__tests__/e2e/scenarios.ts
|
|
1392
|
+
import { logger as logger9, createUniqueUuid as createUniqueUuid5 } from "@elizaos/core";
|
|
1393
|
+
var farcasterE2EScenarios = [
|
|
1394
|
+
{
|
|
1395
|
+
name: "Farcaster Plugin - Agent Introduction",
|
|
1396
|
+
async fn(runtime) {
|
|
1397
|
+
var _a;
|
|
1398
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1399
|
+
if (!service) {
|
|
1400
|
+
throw new Error("Farcaster service not initialized");
|
|
1401
|
+
}
|
|
1402
|
+
const postService = service.getPostService(runtime.agentId);
|
|
1403
|
+
if (!postService) {
|
|
1404
|
+
throw new Error("PostService not available");
|
|
1405
|
+
}
|
|
1406
|
+
const introText = `Hello Farcaster! I'm ${runtime.character.name}, an AI agent powered by ElizaOS. Looking forward to connecting with you all! \u{1F916}`;
|
|
1407
|
+
const cast = await postService.createPost({
|
|
1408
|
+
agentId: runtime.agentId,
|
|
1409
|
+
roomId: createUniqueUuid5(runtime, "farcaster-timeline"),
|
|
1410
|
+
text: introText
|
|
1411
|
+
});
|
|
1412
|
+
if (!cast || !cast.id || !cast.text || !((_a = cast.metadata) == null ? void 0 : _a.castHash)) {
|
|
1413
|
+
throw new Error("Failed to create introduction cast");
|
|
1414
|
+
}
|
|
1415
|
+
logger9.info(`Posted introduction cast: ${cast.metadata.castHash}`);
|
|
1416
|
+
const manager = service.getActiveManagers().get(runtime.agentId);
|
|
1417
|
+
if (!manager) {
|
|
1418
|
+
throw new Error("Manager not found for agent");
|
|
1419
|
+
}
|
|
1420
|
+
const fid = parseInt(runtime.getSetting("FARCASTER_FID"));
|
|
1421
|
+
const profile = await manager.client.getProfile(fid);
|
|
1422
|
+
if (!profile || profile.fid !== fid) {
|
|
1423
|
+
throw new Error("Profile fetch failed or FID mismatch");
|
|
1424
|
+
}
|
|
1425
|
+
logger9.info(`Agent profile verified: @${profile.username} (FID: ${profile.fid})`);
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
name: "Farcaster Plugin - Timeline Monitoring",
|
|
1430
|
+
async fn(runtime) {
|
|
1431
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1432
|
+
if (!service) {
|
|
1433
|
+
throw new Error("Farcaster service not initialized");
|
|
1434
|
+
}
|
|
1435
|
+
const postService = service.getPostService(runtime.agentId);
|
|
1436
|
+
if (!postService) {
|
|
1437
|
+
throw new Error("PostService not available");
|
|
1438
|
+
}
|
|
1439
|
+
const casts = await postService.getPosts({
|
|
1440
|
+
agentId: runtime.agentId,
|
|
1441
|
+
limit: 10
|
|
1442
|
+
});
|
|
1443
|
+
if (!Array.isArray(casts)) {
|
|
1444
|
+
throw new Error("getPosts did not return an array");
|
|
1445
|
+
}
|
|
1446
|
+
logger9.info(`Found ${casts.length} casts in timeline`);
|
|
1447
|
+
if (casts.length > 0) {
|
|
1448
|
+
const firstCast = casts[0];
|
|
1449
|
+
if (!firstCast.id || !firstCast.username || !firstCast.text) {
|
|
1450
|
+
throw new Error("Cast missing required fields");
|
|
1451
|
+
}
|
|
1452
|
+
logger9.info(`Latest cast by @${firstCast.username}: ${firstCast.text.substring(0, 50)}...`);
|
|
1453
|
+
}
|
|
1454
|
+
const mentions = await postService.getMentions(runtime.agentId, { limit: 5 });
|
|
1455
|
+
if (!Array.isArray(mentions)) {
|
|
1456
|
+
throw new Error("getMentions did not return an array");
|
|
1457
|
+
}
|
|
1458
|
+
logger9.info(`Found ${mentions.length} mentions`);
|
|
1459
|
+
}
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
name: "Farcaster Plugin - Message Send and Retrieve",
|
|
1463
|
+
async fn(runtime) {
|
|
1464
|
+
var _a;
|
|
1465
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1466
|
+
if (!service) {
|
|
1467
|
+
throw new Error("Farcaster service not initialized");
|
|
1468
|
+
}
|
|
1469
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
1470
|
+
if (!messageService) {
|
|
1471
|
+
throw new Error("MessageService not available");
|
|
1472
|
+
}
|
|
1473
|
+
const roomId = createUniqueUuid5(runtime, "test-conversation");
|
|
1474
|
+
const message = await messageService.sendMessage({
|
|
1475
|
+
agentId: runtime.agentId,
|
|
1476
|
+
roomId,
|
|
1477
|
+
text: "Testing message send and retrieve with ElizaOS Farcaster plugin! \u{1F9EA}",
|
|
1478
|
+
type: "CAST" /* CAST */
|
|
1479
|
+
});
|
|
1480
|
+
if (!message || !message.id || !((_a = message.metadata) == null ? void 0 : _a.castHash)) {
|
|
1481
|
+
throw new Error("Failed to send message or missing metadata");
|
|
1482
|
+
}
|
|
1483
|
+
logger9.info(`Sent cast with hash: ${message.metadata.castHash}`);
|
|
1484
|
+
const castHash = message.metadata.castHash;
|
|
1485
|
+
const retrieved = await messageService.getMessage(castHash, runtime.agentId);
|
|
1486
|
+
if (!retrieved || retrieved.text !== message.text) {
|
|
1487
|
+
throw new Error("Failed to retrieve message or content mismatch");
|
|
1488
|
+
}
|
|
1489
|
+
logger9.info("Successfully retrieved message by hash");
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
name: "Farcaster Plugin - Reply Threading",
|
|
1494
|
+
async fn(runtime) {
|
|
1495
|
+
var _a, _b;
|
|
1496
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1497
|
+
if (!service) {
|
|
1498
|
+
throw new Error("Farcaster service not initialized");
|
|
1499
|
+
}
|
|
1500
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
1501
|
+
const postService = service.getPostService(runtime.agentId);
|
|
1502
|
+
if (!messageService || !postService) {
|
|
1503
|
+
throw new Error("Services not available");
|
|
1504
|
+
}
|
|
1505
|
+
const originalCast = await postService.createPost({
|
|
1506
|
+
agentId: runtime.agentId,
|
|
1507
|
+
roomId: createUniqueUuid5(runtime, "reply-test"),
|
|
1508
|
+
text: "This is a test cast for reply threading \u{1F9F5}"
|
|
1509
|
+
});
|
|
1510
|
+
if (!originalCast || !((_a = originalCast.metadata) == null ? void 0 : _a.castHash)) {
|
|
1511
|
+
throw new Error("Failed to create original cast");
|
|
1512
|
+
}
|
|
1513
|
+
const reply = await messageService.sendMessage({
|
|
1514
|
+
agentId: runtime.agentId,
|
|
1515
|
+
roomId: originalCast.roomId,
|
|
1516
|
+
text: "This is a test reply maintaining thread context! \u{1F4AC}",
|
|
1517
|
+
type: "REPLY",
|
|
1518
|
+
replyToId: originalCast.metadata.castHash,
|
|
1519
|
+
metadata: {
|
|
1520
|
+
parentHash: originalCast.metadata.castHash
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
if (!reply || !reply.inReplyTo || !((_b = reply.metadata) == null ? void 0 : _b.castHash)) {
|
|
1524
|
+
throw new Error("Failed to create reply or missing thread context");
|
|
1525
|
+
}
|
|
1526
|
+
logger9.info(`Created reply ${reply.metadata.castHash} to ${originalCast.metadata.castHash}`);
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
name: "Farcaster Plugin - Action Execution",
|
|
1531
|
+
async fn(runtime) {
|
|
1532
|
+
const { sendCastAction: sendCastAction2 } = await import("./sendCast-OW6DBKQB.js");
|
|
1533
|
+
const mockMessage = {
|
|
1534
|
+
id: createUniqueUuid5(runtime, "test-message"),
|
|
1535
|
+
agentId: runtime.agentId,
|
|
1536
|
+
roomId: createUniqueUuid5(runtime, "test-room"),
|
|
1537
|
+
entityId: runtime.agentId,
|
|
1538
|
+
content: {
|
|
1539
|
+
text: "Can you post about the ElizaOS framework on Farcaster?"
|
|
1540
|
+
},
|
|
1541
|
+
createdAt: Date.now()
|
|
1542
|
+
};
|
|
1543
|
+
const shouldExecute = await sendCastAction2.validate(runtime, mockMessage);
|
|
1544
|
+
if (!shouldExecute) {
|
|
1545
|
+
throw new Error("SEND_CAST action validation failed");
|
|
1546
|
+
}
|
|
1547
|
+
const result = await sendCastAction2.handler(runtime, mockMessage);
|
|
1548
|
+
if (!result) {
|
|
1549
|
+
throw new Error("SEND_CAST action execution failed");
|
|
1550
|
+
}
|
|
1551
|
+
logger9.info("Successfully validated and executed SEND_CAST action");
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
name: "Farcaster Plugin - Provider Context",
|
|
1556
|
+
async fn(runtime) {
|
|
1557
|
+
var _a, _b;
|
|
1558
|
+
const { farcasterProfileProvider: farcasterProfileProvider2 } = await import("./profileProvider-TNRU42OO.js");
|
|
1559
|
+
const { farcasterTimelineProvider: farcasterTimelineProvider2 } = await import("./timelineProvider-GPRPFEVJ.js");
|
|
1560
|
+
const mockMessage = {
|
|
1561
|
+
id: createUniqueUuid5(runtime, "test-message"),
|
|
1562
|
+
agentId: runtime.agentId,
|
|
1563
|
+
roomId: createUniqueUuid5(runtime, "test-room"),
|
|
1564
|
+
entityId: runtime.agentId,
|
|
1565
|
+
content: { text: "test" },
|
|
1566
|
+
createdAt: Date.now()
|
|
1567
|
+
};
|
|
1568
|
+
const profileContext = await farcasterProfileProvider2.get(runtime, mockMessage, { values: [], data: {}, text: "" });
|
|
1569
|
+
if (!profileContext || !profileContext.text || ((_a = profileContext.data) == null ? void 0 : _a.available) === void 0) {
|
|
1570
|
+
throw new Error("Profile provider returned invalid context");
|
|
1571
|
+
}
|
|
1572
|
+
logger9.info(`Profile provider: ${profileContext.text}`);
|
|
1573
|
+
const timelineContext = await farcasterTimelineProvider2.get(runtime, mockMessage, { values: [], data: {}, text: "" });
|
|
1574
|
+
if (!timelineContext || !timelineContext.text || ((_b = timelineContext.data) == null ? void 0 : _b.available) === void 0) {
|
|
1575
|
+
throw new Error("Timeline provider returned invalid context");
|
|
1576
|
+
}
|
|
1577
|
+
logger9.info(`Timeline provider: ${timelineContext.text}`);
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
name: "Farcaster Plugin - Rate Limit Handling",
|
|
1582
|
+
async fn(runtime) {
|
|
1583
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1584
|
+
if (!service) {
|
|
1585
|
+
throw new Error("Farcaster service not initialized");
|
|
1586
|
+
}
|
|
1587
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
1588
|
+
if (!messageService) {
|
|
1589
|
+
throw new Error("MessageService not available");
|
|
1590
|
+
}
|
|
1591
|
+
const promises = [];
|
|
1592
|
+
for (let i = 0; i < 3; i++) {
|
|
1593
|
+
promises.push(
|
|
1594
|
+
messageService.sendMessage({
|
|
1595
|
+
agentId: runtime.agentId,
|
|
1596
|
+
roomId: createUniqueUuid5(runtime, "rate-limit-test"),
|
|
1597
|
+
text: `Rate limit test message ${i + 1}`,
|
|
1598
|
+
type: "CAST" /* CAST */
|
|
1599
|
+
}).catch((error) => {
|
|
1600
|
+
logger9.warn(`Expected rate limit error: ${error.message}`);
|
|
1601
|
+
return null;
|
|
1602
|
+
})
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
const results = await Promise.all(promises);
|
|
1606
|
+
const successfulSends = results.filter((r) => r !== null);
|
|
1607
|
+
if (successfulSends.length === 0) {
|
|
1608
|
+
throw new Error("All messages failed - check if rate limiting is too strict");
|
|
1609
|
+
}
|
|
1610
|
+
logger9.info(`Successfully sent ${successfulSends.length} out of ${promises.length} messages`);
|
|
1611
|
+
}
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
name: "Farcaster Plugin - Service Health Check",
|
|
1615
|
+
async fn(runtime) {
|
|
1616
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1617
|
+
if (!service) {
|
|
1618
|
+
throw new Error("Farcaster service not initialized");
|
|
1619
|
+
}
|
|
1620
|
+
const health = await service.healthCheck();
|
|
1621
|
+
if (!health || health.healthy === void 0 || !health.details) {
|
|
1622
|
+
throw new Error("Health check returned invalid data");
|
|
1623
|
+
}
|
|
1624
|
+
logger9.info(`Service health: ${health.healthy ? "Healthy" : "Unhealthy"}`);
|
|
1625
|
+
logger9.info(`Active managers: ${health.details.activeManagers}`);
|
|
1626
|
+
if (!health.healthy) {
|
|
1627
|
+
logger9.warn("Service reported unhealthy status:", health.details);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
name: "Farcaster Plugin - Should Send a Real Cast",
|
|
1633
|
+
async fn(runtime) {
|
|
1634
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1635
|
+
if (!service) {
|
|
1636
|
+
throw new Error("FarcasterService not found");
|
|
1637
|
+
}
|
|
1638
|
+
const postService = service.getPostService(runtime.agentId);
|
|
1639
|
+
if (!postService) {
|
|
1640
|
+
throw new Error("PostService not available");
|
|
1641
|
+
}
|
|
1642
|
+
const uniqueMessage = `This is a real E2E test cast from ElizaOS! ID: ${createUniqueUuid5(runtime, "e2e-cast")}`;
|
|
1643
|
+
logger9.info(`Attempting to post cast: "${uniqueMessage}"`);
|
|
1644
|
+
const cast = await postService.createPost({
|
|
1645
|
+
agentId: runtime.agentId,
|
|
1646
|
+
roomId: createUniqueUuid5(runtime, "farcaster-e2e-test"),
|
|
1647
|
+
text: uniqueMessage
|
|
1648
|
+
});
|
|
1649
|
+
if (!cast || !cast.id) {
|
|
1650
|
+
throw new Error("E2E test failed to create a real cast.");
|
|
1651
|
+
}
|
|
1652
|
+
logger9.success(`Successfully posted E2E test cast with ID: ${cast.id}`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
];
|
|
1656
|
+
|
|
1657
|
+
// src/__tests__/suite.ts
|
|
944
1658
|
var FarcasterTestSuite = class {
|
|
945
|
-
name = "
|
|
1659
|
+
name = "Farcaster Plugin Tests";
|
|
1660
|
+
description = "Test suite for Farcaster plugin functionality";
|
|
946
1661
|
manager = null;
|
|
947
1662
|
tests;
|
|
948
1663
|
/**
|
|
@@ -950,23 +1665,76 @@ var FarcasterTestSuite = class {
|
|
|
950
1665
|
* Initializes an array of test functions to be executed.
|
|
951
1666
|
*/
|
|
952
1667
|
constructor() {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1668
|
+
const hasRealCredentials = !!(process.env.FARCASTER_FID && process.env.FARCASTER_SIGNER_UUID && process.env.FARCASTER_NEYNAR_API_KEY);
|
|
1669
|
+
logger10.info("=== Farcaster Test Suite Configuration ===");
|
|
1670
|
+
logger10.info(`FID: ${process.env.FARCASTER_FID ? "\u2713 Found" : "\u2717 Missing"}`);
|
|
1671
|
+
logger10.info(`Signer UUID: ${process.env.FARCASTER_SIGNER_UUID ? "\u2713 Found" : "\u2717 Missing"}`);
|
|
1672
|
+
logger10.info(`API Key: ${process.env.FARCASTER_NEYNAR_API_KEY ? "\u2713 Found" : "\u2717 Missing"}`);
|
|
1673
|
+
logger10.info(`Dry Run: ${process.env.FARCASTER_DRY_RUN || "not set (defaults to false)"}`);
|
|
1674
|
+
logger10.info("=========================================");
|
|
1675
|
+
if (hasRealCredentials) {
|
|
1676
|
+
logger10.success("\u2705 Running with real Farcaster credentials");
|
|
1677
|
+
this.tests = farcasterE2EScenarios;
|
|
1678
|
+
} else {
|
|
1679
|
+
logger10.warn("\u26A0\uFE0F Farcaster credentials not found in environment variables");
|
|
1680
|
+
logger10.warn("\u26A0\uFE0F Tests will run in mock mode");
|
|
1681
|
+
logger10.warn("\u26A0\uFE0F To run real tests, set FARCASTER_FID, FARCASTER_SIGNER_UUID, and FARCASTER_NEYNAR_API_KEY");
|
|
1682
|
+
this.tests = [
|
|
1683
|
+
{
|
|
1684
|
+
name: "Mock: Check Farcaster Configuration",
|
|
1685
|
+
fn: this.testMockConfiguration.bind(this)
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
name: "Mock: Service Initialization",
|
|
1689
|
+
fn: this.testMockServiceInit.bind(this)
|
|
1690
|
+
}
|
|
1691
|
+
];
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Test that checks if Farcaster is properly configured
|
|
1696
|
+
*/
|
|
1697
|
+
async testMockConfiguration(runtime) {
|
|
1698
|
+
logger10.info("Running mock configuration test");
|
|
1699
|
+
const fid = runtime.getSetting("FARCASTER_FID");
|
|
1700
|
+
const signerUuid = runtime.getSetting("FARCASTER_SIGNER_UUID");
|
|
1701
|
+
const apiKey = runtime.getSetting("FARCASTER_NEYNAR_API_KEY");
|
|
1702
|
+
logger10.info("Runtime settings check:");
|
|
1703
|
+
logger10.info(`- FID from runtime: ${fid ? "Found" : "Not found"}`);
|
|
1704
|
+
logger10.info(`- Signer UUID from runtime: ${signerUuid ? "Found" : "Not found"}`);
|
|
1705
|
+
logger10.info(`- API Key from runtime: ${apiKey ? "Found" : "Not found"}`);
|
|
1706
|
+
if (!fid || !signerUuid || !apiKey) {
|
|
1707
|
+
logger10.info("Farcaster not configured - this is expected in mock mode");
|
|
1708
|
+
logger10.info("To enable real tests, configure the following:");
|
|
1709
|
+
logger10.info("- FARCASTER_FID: Your Farcaster ID");
|
|
1710
|
+
logger10.info("- FARCASTER_SIGNER_UUID: Neynar signer UUID");
|
|
1711
|
+
logger10.info("- FARCASTER_NEYNAR_API_KEY: Neynar API key");
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Test service initialization without real credentials
|
|
1716
|
+
*/
|
|
1717
|
+
async testMockServiceInit(runtime) {
|
|
1718
|
+
logger10.info("Running mock service initialization test");
|
|
1719
|
+
try {
|
|
1720
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1721
|
+
if (!service) {
|
|
1722
|
+
logger10.info("Farcaster service not available - this is expected without credentials");
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
logger10.info("Farcaster service is registered but may not be fully initialized");
|
|
1726
|
+
if (typeof service.getMessageService === "function") {
|
|
1727
|
+
logger10.info("\u2713 getMessageService method exists");
|
|
1728
|
+
}
|
|
1729
|
+
if (typeof service.getPostService === "function") {
|
|
1730
|
+
logger10.info("\u2713 getPostService method exists");
|
|
1731
|
+
}
|
|
1732
|
+
if (typeof service.healthCheck === "function") {
|
|
1733
|
+
logger10.info("\u2713 healthCheck method exists");
|
|
968
1734
|
}
|
|
969
|
-
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
logger10.info("Service initialization check completed");
|
|
1737
|
+
}
|
|
970
1738
|
}
|
|
971
1739
|
/**
|
|
972
1740
|
* Asynchronously initializes the Farcaster client for the provided agent runtime.
|
|
@@ -982,7 +1750,7 @@ var FarcasterTestSuite = class {
|
|
|
982
1750
|
}
|
|
983
1751
|
this.manager = service.managers.get(runtime.agentId);
|
|
984
1752
|
if (this.manager) {
|
|
985
|
-
|
|
1753
|
+
logger10.debug("FarcasterAgentManager initialized successfully.");
|
|
986
1754
|
} else {
|
|
987
1755
|
throw new Error("FarcasterAgentManager failed to initialize.");
|
|
988
1756
|
}
|
|
@@ -1009,7 +1777,7 @@ var FarcasterTestSuite = class {
|
|
|
1009
1777
|
if (!profile || !profile.fid) {
|
|
1010
1778
|
throw new Error("Profile fetch failed.");
|
|
1011
1779
|
}
|
|
1012
|
-
|
|
1780
|
+
logger10.log("Successfully fetched Farcaster profile:", profile);
|
|
1013
1781
|
} catch (error) {
|
|
1014
1782
|
throw new Error(`Error fetching Farcaster profile: ${error}`);
|
|
1015
1783
|
}
|
|
@@ -1035,7 +1803,7 @@ var FarcasterTestSuite = class {
|
|
|
1035
1803
|
if (!result.timeline || result.timeline.length === 0) {
|
|
1036
1804
|
throw new Error("No casts in timeline.");
|
|
1037
1805
|
}
|
|
1038
|
-
|
|
1806
|
+
logger10.log(`Successfully fetched ${result.timeline.length} casts from timeline.`);
|
|
1039
1807
|
} catch (error) {
|
|
1040
1808
|
throw new Error(`Error fetching timeline: ${error}`);
|
|
1041
1809
|
}
|
|
@@ -1059,7 +1827,7 @@ var FarcasterTestSuite = class {
|
|
|
1059
1827
|
if (!result || result.length === 0) {
|
|
1060
1828
|
throw new Error("Cast posting failed.");
|
|
1061
1829
|
}
|
|
1062
|
-
|
|
1830
|
+
logger10.success("Successfully posted a test cast.");
|
|
1063
1831
|
} catch (error) {
|
|
1064
1832
|
throw new Error(`Error posting a cast: ${error}`);
|
|
1065
1833
|
}
|
|
@@ -1087,7 +1855,7 @@ var FarcasterTestSuite = class {
|
|
|
1087
1855
|
if (!result || result.length === 0) {
|
|
1088
1856
|
throw new Error("Cast with image posting failed.");
|
|
1089
1857
|
}
|
|
1090
|
-
|
|
1858
|
+
logger10.success("Successfully posted a test cast with image.");
|
|
1091
1859
|
} catch (error) {
|
|
1092
1860
|
throw new Error(`Error posting a cast with image: ${error}`);
|
|
1093
1861
|
}
|
|
@@ -1115,15 +1883,15 @@ var FarcasterTestSuite = class {
|
|
|
1115
1883
|
},
|
|
1116
1884
|
timestamp: /* @__PURE__ */ new Date()
|
|
1117
1885
|
};
|
|
1118
|
-
const memoryId =
|
|
1886
|
+
const memoryId = createUniqueUuid6(runtime, testCast.hash);
|
|
1119
1887
|
const memory = {
|
|
1120
1888
|
id: memoryId,
|
|
1121
1889
|
agentId: runtime.agentId,
|
|
1122
1890
|
content: {
|
|
1123
1891
|
text: testCast.text
|
|
1124
1892
|
},
|
|
1125
|
-
entityId:
|
|
1126
|
-
roomId:
|
|
1893
|
+
entityId: createUniqueUuid6(runtime, String(testCast.authorFid)),
|
|
1894
|
+
roomId: createUniqueUuid6(runtime, "test-room"),
|
|
1127
1895
|
createdAt: testCast.timestamp.getTime()
|
|
1128
1896
|
};
|
|
1129
1897
|
runtime.emitEvent("farcaster.mention_received", {
|
|
@@ -1132,7 +1900,7 @@ var FarcasterTestSuite = class {
|
|
|
1132
1900
|
cast: testCast,
|
|
1133
1901
|
source: "farcaster"
|
|
1134
1902
|
});
|
|
1135
|
-
|
|
1903
|
+
logger10.success("Successfully simulated cast response handling");
|
|
1136
1904
|
} catch (error) {
|
|
1137
1905
|
throw new Error(`Error handling cast response: ${error}`);
|
|
1138
1906
|
}
|
|
@@ -1146,18 +1914,313 @@ var FarcasterTestSuite = class {
|
|
|
1146
1914
|
*/
|
|
1147
1915
|
async generateRandomCastContent(runtime, context = "general") {
|
|
1148
1916
|
const prompt = `Generate a short, interesting cast about ${context} (max 280 chars).`;
|
|
1149
|
-
const result = await runtime.useModel(
|
|
1917
|
+
const result = await runtime.useModel(ModelType3.TEXT_SMALL, {
|
|
1150
1918
|
prompt
|
|
1151
1919
|
});
|
|
1152
1920
|
return result.substring(0, 280);
|
|
1153
1921
|
}
|
|
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
|
+
async testMessageService(runtime) {
|
|
1929
|
+
try {
|
|
1930
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1931
|
+
if (!service) {
|
|
1932
|
+
throw new Error("Farcaster service not found");
|
|
1933
|
+
}
|
|
1934
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
1935
|
+
if (!messageService) {
|
|
1936
|
+
throw new Error("MessageService not initialized");
|
|
1937
|
+
}
|
|
1938
|
+
const messages = await messageService.getMessages({
|
|
1939
|
+
agentId: runtime.agentId,
|
|
1940
|
+
limit: 5
|
|
1941
|
+
});
|
|
1942
|
+
logger10.log(`Retrieved ${messages.length} messages from MessageService`);
|
|
1943
|
+
const testText = await this.generateRandomCastContent(runtime, "message_service_test");
|
|
1944
|
+
const message = await messageService.sendMessage({
|
|
1945
|
+
agentId: runtime.agentId,
|
|
1946
|
+
roomId: createUniqueUuid6(runtime, "test-room"),
|
|
1947
|
+
text: testText,
|
|
1948
|
+
type: "CAST" /* CAST */
|
|
1949
|
+
});
|
|
1950
|
+
if (!message || !message.id) {
|
|
1951
|
+
throw new Error("Failed to send message via MessageService");
|
|
1952
|
+
}
|
|
1953
|
+
logger10.success("MessageService test completed successfully");
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
throw new Error(`Error testing MessageService: ${error}`);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
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
|
+
async testPostService(runtime) {
|
|
1965
|
+
try {
|
|
1966
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
1967
|
+
if (!service) {
|
|
1968
|
+
throw new Error("Farcaster service not found");
|
|
1969
|
+
}
|
|
1970
|
+
const castService = service.getCastService(runtime.agentId);
|
|
1971
|
+
if (!castService) {
|
|
1972
|
+
throw new Error("CastService not initialized");
|
|
1973
|
+
}
|
|
1974
|
+
const casts = await castService.getCasts({
|
|
1975
|
+
agentId: runtime.agentId,
|
|
1976
|
+
limit: 5
|
|
1977
|
+
});
|
|
1978
|
+
logger10.log(`Retrieved ${casts.length} casts from CastService`);
|
|
1979
|
+
const testText = await this.generateRandomCastContent(runtime, "cast_service_test");
|
|
1980
|
+
const cast = await castService.createCast({
|
|
1981
|
+
agentId: runtime.agentId,
|
|
1982
|
+
roomId: createUniqueUuid6(runtime, "test-room"),
|
|
1983
|
+
text: testText
|
|
1984
|
+
});
|
|
1985
|
+
if (!cast || !cast.id) {
|
|
1986
|
+
throw new Error("Failed to create cast via CastService");
|
|
1987
|
+
}
|
|
1988
|
+
logger10.success("CastService test completed successfully");
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
throw new Error(`Error testing PostService: ${error}`);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
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
|
+
async testRealAccountPosting(runtime) {
|
|
2000
|
+
var _a;
|
|
2001
|
+
try {
|
|
2002
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2003
|
+
if (!service) {
|
|
2004
|
+
throw new Error("Farcaster service not found");
|
|
2005
|
+
}
|
|
2006
|
+
const postService = service.getPostService(runtime.agentId);
|
|
2007
|
+
if (!postService) {
|
|
2008
|
+
throw new Error("PostService not initialized");
|
|
2009
|
+
}
|
|
2010
|
+
const testPosts = [
|
|
2011
|
+
"Testing ElizaOS Farcaster integration! \u{1F680} #ElizaOS",
|
|
2012
|
+
"AI agents are the future of social media engagement \u{1F916}",
|
|
2013
|
+
"Building amazing things with the ElizaOS framework \u{1F4BB}"
|
|
2014
|
+
];
|
|
2015
|
+
for (const text of testPosts) {
|
|
2016
|
+
const post = await postService.createPost({
|
|
2017
|
+
agentId: runtime.agentId,
|
|
2018
|
+
roomId: createUniqueUuid6(runtime, "real-test"),
|
|
2019
|
+
text
|
|
2020
|
+
});
|
|
2021
|
+
if (!post || !post.id || !((_a = post.metadata) == null ? void 0 : _a.castHash)) {
|
|
2022
|
+
throw new Error("Failed to create real post");
|
|
2023
|
+
}
|
|
2024
|
+
logger10.success(`Posted real cast: ${post.metadata.castHash}`);
|
|
2025
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2026
|
+
}
|
|
2027
|
+
logger10.success("Real account posting test completed successfully");
|
|
2028
|
+
} catch (error) {
|
|
2029
|
+
throw new Error(`Error testing real account posting: ${error}`);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
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
|
+
async testRealAccountInteractions(runtime) {
|
|
2039
|
+
var _a, _b;
|
|
2040
|
+
try {
|
|
2041
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2042
|
+
if (!service) {
|
|
2043
|
+
throw new Error("Farcaster service not found");
|
|
2044
|
+
}
|
|
2045
|
+
const postService = service.getPostService(runtime.agentId);
|
|
2046
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
2047
|
+
if (!postService || !messageService) {
|
|
2048
|
+
throw new Error("Services not initialized");
|
|
2049
|
+
}
|
|
2050
|
+
const mentions = await postService.getMentions(runtime.agentId, { limit: 10 });
|
|
2051
|
+
logger10.log(`Found ${mentions.length} mentions`);
|
|
2052
|
+
const timeline = await postService.getPosts({
|
|
2053
|
+
agentId: runtime.agentId,
|
|
2054
|
+
limit: 20
|
|
2055
|
+
});
|
|
2056
|
+
logger10.log(`Found ${timeline.length} timeline posts`);
|
|
2057
|
+
if (timeline.length > 0) {
|
|
2058
|
+
const targetPost = timeline[0];
|
|
2059
|
+
if ((_a = targetPost.metadata) == null ? void 0 : _a.castHash) {
|
|
2060
|
+
const reply = await messageService.sendMessage({
|
|
2061
|
+
agentId: runtime.agentId,
|
|
2062
|
+
roomId: targetPost.roomId,
|
|
2063
|
+
text: "Great post! Testing real interactions with ElizaOS \u{1F389}",
|
|
2064
|
+
type: "REPLY" /* REPLY */,
|
|
2065
|
+
replyToId: targetPost.metadata.castHash,
|
|
2066
|
+
metadata: {
|
|
2067
|
+
parentHash: targetPost.metadata.castHash
|
|
2068
|
+
}
|
|
2069
|
+
});
|
|
2070
|
+
if (reply && ((_b = reply.metadata) == null ? void 0 : _b.castHash)) {
|
|
2071
|
+
logger10.success(`Replied to cast with: ${reply.metadata.castHash}`);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
logger10.success("Real account interactions test completed successfully");
|
|
2076
|
+
} catch (error) {
|
|
2077
|
+
throw new Error(`Error testing real account interactions: ${error}`);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
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
|
+
async testMessageMetadataTracking(runtime) {
|
|
2087
|
+
var _a, _b;
|
|
2088
|
+
try {
|
|
2089
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2090
|
+
if (!service) {
|
|
2091
|
+
throw new Error("Farcaster service not found");
|
|
2092
|
+
}
|
|
2093
|
+
const messageService = service.getMessageService(runtime.agentId);
|
|
2094
|
+
if (!messageService) {
|
|
2095
|
+
throw new Error("MessageService not initialized");
|
|
2096
|
+
}
|
|
2097
|
+
const testMessage = await messageService.sendMessage({
|
|
2098
|
+
agentId: runtime.agentId,
|
|
2099
|
+
roomId: createUniqueUuid6(runtime, "metadata-test"),
|
|
2100
|
+
text: "Testing metadata tracking with ElizaOS",
|
|
2101
|
+
type: "CAST" /* CAST */
|
|
2102
|
+
});
|
|
2103
|
+
if (!testMessage || !((_a = testMessage.metadata) == null ? void 0 : _a.castHash)) {
|
|
2104
|
+
throw new Error("Failed to send test message");
|
|
2105
|
+
}
|
|
2106
|
+
const castHash = testMessage.metadata.castHash;
|
|
2107
|
+
logger10.log(`Sent message with cast hash: ${castHash}`);
|
|
2108
|
+
const retrievedMessage = await messageService.getMessage(castHash, runtime.agentId);
|
|
2109
|
+
if (!retrievedMessage) {
|
|
2110
|
+
throw new Error("Failed to retrieve message by hash");
|
|
2111
|
+
}
|
|
2112
|
+
if (((_b = retrievedMessage.metadata) == null ? void 0 : _b.castHash) !== castHash) {
|
|
2113
|
+
throw new Error("Metadata mismatch in retrieved message");
|
|
2114
|
+
}
|
|
2115
|
+
logger10.success("Message metadata tracking test completed successfully");
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
throw new Error(`Error testing message metadata tracking: ${error}`);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
|
|
2122
|
+
// src/actions/replyCast.ts
|
|
2123
|
+
import {
|
|
2124
|
+
logger as logger11
|
|
2125
|
+
} from "@elizaos/core";
|
|
2126
|
+
var replyCastAction = {
|
|
2127
|
+
name: "REPLY_TO_CAST",
|
|
2128
|
+
description: "Replies to a cast on Farcaster",
|
|
2129
|
+
examples: [
|
|
2130
|
+
[
|
|
2131
|
+
{
|
|
2132
|
+
name: "user",
|
|
2133
|
+
content: { text: "Someone asked about ElizaOS on Farcaster, can you reply?" }
|
|
2134
|
+
},
|
|
2135
|
+
{
|
|
2136
|
+
name: "assistant",
|
|
2137
|
+
content: {
|
|
2138
|
+
text: "I'll reply to their question about ElizaOS.",
|
|
2139
|
+
actions: ["REPLY_TO_CAST"]
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
],
|
|
2143
|
+
[
|
|
2144
|
+
{
|
|
2145
|
+
name: "user",
|
|
2146
|
+
content: { text: "Reply to that cast and thank them for the feedback" }
|
|
2147
|
+
},
|
|
2148
|
+
{
|
|
2149
|
+
name: "assistant",
|
|
2150
|
+
content: {
|
|
2151
|
+
text: "I'll reply with a thank you message.",
|
|
2152
|
+
actions: ["REPLY_TO_CAST"]
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
]
|
|
2156
|
+
],
|
|
2157
|
+
validate: async (runtime, message) => {
|
|
2158
|
+
var _a;
|
|
2159
|
+
const text = ((_a = message.content.text) == null ? void 0 : _a.toLowerCase()) || "";
|
|
2160
|
+
const keywords = ["reply", "respond", "answer", "comment"];
|
|
2161
|
+
const hasKeyword = keywords.some((keyword) => text.includes(keyword));
|
|
2162
|
+
const hasParentCast = !!(message.content.metadata && message.content.metadata.parentCastHash);
|
|
2163
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2164
|
+
const isServiceAvailable = !!(service == null ? void 0 : service.getMessageService(runtime.agentId));
|
|
2165
|
+
return hasKeyword && (hasParentCast || isServiceAvailable);
|
|
2166
|
+
},
|
|
2167
|
+
handler: async (runtime, message, state) => {
|
|
2168
|
+
var _a;
|
|
2169
|
+
try {
|
|
2170
|
+
const service = runtime.getService(FARCASTER_SERVICE_NAME);
|
|
2171
|
+
const messageService = service == null ? void 0 : service.getMessageService(runtime.agentId);
|
|
2172
|
+
if (!messageService) {
|
|
2173
|
+
logger11.error("[REPLY_TO_CAST] MessageService not available");
|
|
2174
|
+
return false;
|
|
2175
|
+
}
|
|
2176
|
+
const parentCastHash = ((_a = message.content.metadata) == null ? void 0 : _a.parentCastHash) || (state == null ? void 0 : state.parentCastHash);
|
|
2177
|
+
if (!parentCastHash) {
|
|
2178
|
+
logger11.error("[REPLY_TO_CAST] No parent cast to reply to");
|
|
2179
|
+
return false;
|
|
2180
|
+
}
|
|
2181
|
+
let replyContent = "";
|
|
2182
|
+
if (state == null ? void 0 : state.replyContent) {
|
|
2183
|
+
replyContent = state.replyContent;
|
|
2184
|
+
} else {
|
|
2185
|
+
const prompt = `Based on this request: "${message.content.text}", generate a helpful and engaging reply for a Farcaster cast (max 320 characters).`;
|
|
2186
|
+
const response = await runtime.useModel("text_large", { prompt });
|
|
2187
|
+
replyContent = typeof response === "string" ? response : response.text || "";
|
|
2188
|
+
}
|
|
2189
|
+
if (replyContent.length > 320) {
|
|
2190
|
+
replyContent = replyContent.substring(0, 317) + "...";
|
|
2191
|
+
}
|
|
2192
|
+
const reply = await messageService.sendMessage({
|
|
2193
|
+
agentId: runtime.agentId,
|
|
2194
|
+
roomId: message.roomId,
|
|
2195
|
+
text: replyContent,
|
|
2196
|
+
type: "REPLY" /* REPLY */,
|
|
2197
|
+
replyToId: parentCastHash,
|
|
2198
|
+
metadata: {
|
|
2199
|
+
parentHash: parentCastHash
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2202
|
+
logger11.info(`[REPLY_TO_CAST] Successfully replied to cast: ${reply.id}`);
|
|
2203
|
+
return true;
|
|
2204
|
+
} catch (error) {
|
|
2205
|
+
logger11.error("[REPLY_TO_CAST] Error replying to cast:", error);
|
|
2206
|
+
return false;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
1154
2209
|
};
|
|
1155
2210
|
|
|
2211
|
+
// src/actions/index.ts
|
|
2212
|
+
var farcasterActions = [sendCastAction, replyCastAction];
|
|
2213
|
+
|
|
2214
|
+
// src/providers/index.ts
|
|
2215
|
+
var farcasterProviders = [farcasterProfileProvider, farcasterTimelineProvider];
|
|
2216
|
+
|
|
1156
2217
|
// src/index.ts
|
|
1157
2218
|
var farcasterPlugin = {
|
|
1158
2219
|
name: "farcaster",
|
|
1159
|
-
description: "Farcaster client plugin",
|
|
2220
|
+
description: "Farcaster client plugin for sending and receiving casts",
|
|
1160
2221
|
services: [FarcasterService],
|
|
2222
|
+
actions: farcasterActions,
|
|
2223
|
+
providers: farcasterProviders,
|
|
1161
2224
|
tests: [new FarcasterTestSuite()]
|
|
1162
2225
|
};
|
|
1163
2226
|
var index_default = farcasterPlugin;
|