@net-protocol/cli 0.1.42 → 0.1.44
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 +131 -0
- package/dist/chat/index.mjs +24 -3
- package/dist/chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +1310 -129
- package/dist/cli/index.mjs.map +1 -1
- package/dist/feed/index.mjs +407 -93
- package/dist/feed/index.mjs.map +1 -1
- package/dist/profile/index.mjs +17 -1
- package/dist/profile/index.mjs.map +1 -1
- package/dist/upvote/index.mjs +24 -1
- package/dist/upvote/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/feed/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk12 from 'chalk';
|
|
2
2
|
import { isCommentTopic, parseCommentData, FeedRegistryClient, FeedClient, AgentRegistryClient, COMMENT_TOPIC_SUFFIX, FEED_TOPIC_PREFIX } from '@net-protocol/feeds';
|
|
3
3
|
import '@net-protocol/chats';
|
|
4
|
-
import { NULL_ADDRESS, getPublicClient, getNetContract, getBaseDataSuffix, getChainRpcUrls, NetClient } from '@net-protocol/core';
|
|
4
|
+
import { NULL_ADDRESS, getPublicClient, getNetContract, getBaseDataSuffix, getChainRpcUrls, getChainBlockExplorer, NetClient, getChainSlug } from '@net-protocol/core';
|
|
5
5
|
import '@net-protocol/storage';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
@@ -91,6 +91,61 @@ function exitWithError(message) {
|
|
|
91
91
|
console.error(chalk12.red(`Error: ${message}`));
|
|
92
92
|
process.exit(1);
|
|
93
93
|
}
|
|
94
|
+
var WEBSITE_BASE = "https://netprotocol.app";
|
|
95
|
+
function chainSlug(chainId) {
|
|
96
|
+
return getChainSlug({ chainId }) ?? null;
|
|
97
|
+
}
|
|
98
|
+
function stripFeedPrefix(topic) {
|
|
99
|
+
const lower = topic.toLowerCase();
|
|
100
|
+
return lower.startsWith("feed-") ? lower.slice(5) : lower;
|
|
101
|
+
}
|
|
102
|
+
function feedUrl(chainId, feedName) {
|
|
103
|
+
const slug = chainSlug(chainId);
|
|
104
|
+
if (!slug) return null;
|
|
105
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/${stripFeedPrefix(feedName)}`;
|
|
106
|
+
}
|
|
107
|
+
function walletUrl(chainId, address) {
|
|
108
|
+
const slug = chainSlug(chainId);
|
|
109
|
+
if (!slug) return null;
|
|
110
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/${address.toLowerCase()}`;
|
|
111
|
+
}
|
|
112
|
+
function profileUrl(chainId, address) {
|
|
113
|
+
const slug = chainSlug(chainId);
|
|
114
|
+
if (!slug) return null;
|
|
115
|
+
return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;
|
|
116
|
+
}
|
|
117
|
+
function explorerTxUrl(chainId, txHash) {
|
|
118
|
+
const base = getChainBlockExplorer({ chainId })?.url;
|
|
119
|
+
if (!base) return null;
|
|
120
|
+
return `${base}/tx/${txHash}`;
|
|
121
|
+
}
|
|
122
|
+
function postIdToCommentParam(postId) {
|
|
123
|
+
const colon = postId.indexOf(":");
|
|
124
|
+
if (colon === -1) return postId;
|
|
125
|
+
return `${postId.slice(0, colon)}-${postId.slice(colon + 1)}`;
|
|
126
|
+
}
|
|
127
|
+
function postPermalink(chainId, opts) {
|
|
128
|
+
const slug = chainSlug(chainId);
|
|
129
|
+
if (!slug) return null;
|
|
130
|
+
const params = new URLSearchParams();
|
|
131
|
+
if (opts.globalIndex != null) {
|
|
132
|
+
params.set("index", String(opts.globalIndex));
|
|
133
|
+
} else if (opts.topic != null && opts.topicIndex != null) {
|
|
134
|
+
params.set("topic", stripFeedPrefix(opts.topic));
|
|
135
|
+
params.set("index", String(opts.topicIndex));
|
|
136
|
+
} else if (opts.user != null && opts.userIndex != null) {
|
|
137
|
+
params.set("user", opts.user.toLowerCase());
|
|
138
|
+
params.set("index", String(opts.userIndex));
|
|
139
|
+
} else {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (opts.commentId) {
|
|
143
|
+
params.set("commentId", postIdToCommentParam(opts.commentId));
|
|
144
|
+
}
|
|
145
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/post?${params.toString()}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/commands/feed/format.ts
|
|
94
149
|
function truncateAddress(address) {
|
|
95
150
|
if (address.length <= 12) return address;
|
|
96
151
|
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
@@ -144,38 +199,70 @@ function formatComment(comment, depth) {
|
|
|
144
199
|
];
|
|
145
200
|
return lines.join("\n");
|
|
146
201
|
}
|
|
147
|
-
function
|
|
202
|
+
function stripFeedPrefix2(topic) {
|
|
203
|
+
const match = topic.match(/^(.+?):comments:/);
|
|
204
|
+
const base = match ? match[1] : topic;
|
|
205
|
+
return base.replace(/^feed-/i, "");
|
|
206
|
+
}
|
|
207
|
+
function postToJson(post, options) {
|
|
208
|
+
const { chainId, topicIndex, userIndex, globalIndex, commentCount } = options;
|
|
209
|
+
const feedName = stripFeedPrefix2(post.topic);
|
|
210
|
+
const postId = `${post.sender}:${post.timestamp}`;
|
|
211
|
+
const permalink = postPermalink(chainId, {
|
|
212
|
+
globalIndex,
|
|
213
|
+
topic: post.topic,
|
|
214
|
+
topicIndex,
|
|
215
|
+
user: userIndex !== void 0 ? post.sender : void 0,
|
|
216
|
+
userIndex
|
|
217
|
+
});
|
|
148
218
|
const result = {
|
|
149
|
-
|
|
219
|
+
postId,
|
|
220
|
+
permalink,
|
|
150
221
|
sender: post.sender,
|
|
222
|
+
senderProfileUrl: profileUrl(chainId, post.sender),
|
|
223
|
+
senderWalletUrl: walletUrl(chainId, post.sender),
|
|
151
224
|
text: post.text,
|
|
152
225
|
timestamp: Number(post.timestamp),
|
|
226
|
+
feed: feedName,
|
|
227
|
+
feedUrl: feedUrl(chainId, feedName),
|
|
153
228
|
topic: post.topic
|
|
154
229
|
};
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
}
|
|
230
|
+
if (topicIndex !== void 0) result.topicIndex = topicIndex;
|
|
231
|
+
if (userIndex !== void 0) result.userIndex = userIndex;
|
|
232
|
+
if (globalIndex !== void 0) result.globalIndex = globalIndex;
|
|
233
|
+
if (commentCount !== void 0) result.commentCount = commentCount;
|
|
234
|
+
if (post.data && post.data !== "0x") result.data = post.data;
|
|
161
235
|
return result;
|
|
162
236
|
}
|
|
163
|
-
function feedToJson(feed, index) {
|
|
164
|
-
|
|
237
|
+
function feedToJson(feed, index, chainId) {
|
|
238
|
+
const result = {
|
|
165
239
|
index,
|
|
166
240
|
feedName: feed.feedName,
|
|
167
241
|
registrant: feed.registrant,
|
|
168
242
|
timestamp: feed.timestamp
|
|
169
243
|
};
|
|
244
|
+
if (chainId !== void 0) {
|
|
245
|
+
result.feedUrl = feedUrl(chainId, feed.feedName);
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
170
248
|
}
|
|
171
|
-
function commentToJson(comment,
|
|
172
|
-
|
|
249
|
+
function commentToJson(comment, options) {
|
|
250
|
+
const { chainId, depth, parentPostUrl } = options;
|
|
251
|
+
const commentParam = `${comment.sender}-${comment.timestamp}`;
|
|
252
|
+
const permalink = parentPostUrl ? `${parentPostUrl}${parentPostUrl.includes("?") ? "&" : "?"}commentId=${commentParam}` : null;
|
|
253
|
+
const result = {
|
|
254
|
+
commentId: `${comment.sender}:${comment.timestamp}`,
|
|
255
|
+
permalink,
|
|
173
256
|
sender: comment.sender,
|
|
257
|
+
senderProfileUrl: profileUrl(chainId, comment.sender),
|
|
174
258
|
text: comment.text,
|
|
175
259
|
timestamp: Number(comment.timestamp),
|
|
176
|
-
depth
|
|
177
|
-
data: comment.data !== "0x" ? comment.data : void 0
|
|
260
|
+
depth
|
|
178
261
|
};
|
|
262
|
+
if (comment.data !== "0x") {
|
|
263
|
+
result.data = comment.data;
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
179
266
|
}
|
|
180
267
|
function formatAgent(agent, index) {
|
|
181
268
|
const timestamp = formatTimestamp(agent.timestamp);
|
|
@@ -185,12 +272,17 @@ function formatAgent(agent, index) {
|
|
|
185
272
|
];
|
|
186
273
|
return lines.join("\n");
|
|
187
274
|
}
|
|
188
|
-
function agentToJson(agent, index) {
|
|
189
|
-
|
|
275
|
+
function agentToJson(agent, index, chainId) {
|
|
276
|
+
const result = {
|
|
190
277
|
index,
|
|
191
278
|
address: agent.address,
|
|
192
279
|
timestamp: agent.timestamp
|
|
193
280
|
};
|
|
281
|
+
if (chainId !== void 0) {
|
|
282
|
+
result.profileUrl = profileUrl(chainId, agent.address);
|
|
283
|
+
result.walletUrl = walletUrl(chainId, agent.address);
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
194
286
|
}
|
|
195
287
|
function printJson(data) {
|
|
196
288
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -208,7 +300,11 @@ async function executeFeedList(options) {
|
|
|
208
300
|
maxFeeds: options.limit ?? 50
|
|
209
301
|
});
|
|
210
302
|
if (options.json) {
|
|
211
|
-
printJson(
|
|
303
|
+
printJson(
|
|
304
|
+
feeds.map(
|
|
305
|
+
(feed, i) => feedToJson(feed, i, readOnlyOptions.chainId)
|
|
306
|
+
)
|
|
307
|
+
);
|
|
212
308
|
} else {
|
|
213
309
|
if (feeds.length === 0) {
|
|
214
310
|
console.log(chalk12.yellow("No registered feeds found"));
|
|
@@ -434,26 +530,31 @@ async function executeFeedRead(feed, options) {
|
|
|
434
530
|
return;
|
|
435
531
|
}
|
|
436
532
|
const fetchLimit = options.sender ? Math.max(limit * 5, 100) : limit;
|
|
437
|
-
|
|
533
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
438
534
|
topic: normalizedFeed,
|
|
439
535
|
maxPosts: fetchLimit
|
|
440
536
|
});
|
|
537
|
+
let postsWithIndex = fetched.messages.map((post, i) => ({
|
|
538
|
+
post,
|
|
539
|
+
topicIndex: fetched.startIndex + i
|
|
540
|
+
}));
|
|
441
541
|
if (options.sender) {
|
|
442
542
|
const senderLower = options.sender.toLowerCase();
|
|
443
|
-
|
|
444
|
-
(post) => post.sender.toLowerCase() === senderLower
|
|
543
|
+
postsWithIndex = postsWithIndex.filter(
|
|
544
|
+
({ post }) => post.sender.toLowerCase() === senderLower
|
|
445
545
|
);
|
|
446
|
-
|
|
546
|
+
postsWithIndex = postsWithIndex.slice(0, limit);
|
|
447
547
|
}
|
|
448
548
|
if (options.unseen) {
|
|
449
549
|
const lastSeen = getLastSeenTimestamp(normalizedFeed);
|
|
450
550
|
const myAddress = getMyAddress();
|
|
451
|
-
|
|
551
|
+
postsWithIndex = postsWithIndex.filter(({ post }) => {
|
|
452
552
|
const isNew = lastSeen === null || Number(post.timestamp) > lastSeen;
|
|
453
553
|
const isFromOther = !myAddress || post.sender.toLowerCase() !== myAddress;
|
|
454
554
|
return isNew && isFromOther;
|
|
455
555
|
});
|
|
456
556
|
}
|
|
557
|
+
const posts = postsWithIndex.map(({ post }) => post);
|
|
457
558
|
if (options.markSeen) {
|
|
458
559
|
const allPosts = await client.getFeedPosts({
|
|
459
560
|
topic: normalizedFeed,
|
|
@@ -469,7 +570,13 @@ async function executeFeedRead(feed, options) {
|
|
|
469
570
|
);
|
|
470
571
|
if (options.json) {
|
|
471
572
|
printJson(
|
|
472
|
-
|
|
573
|
+
postsWithIndex.map(
|
|
574
|
+
({ post, topicIndex }, i) => postToJson(post, {
|
|
575
|
+
chainId: readOnlyOptions.chainId,
|
|
576
|
+
topicIndex,
|
|
577
|
+
commentCount: commentCounts[i]
|
|
578
|
+
})
|
|
579
|
+
)
|
|
473
580
|
);
|
|
474
581
|
} else {
|
|
475
582
|
if (posts.length === 0) {
|
|
@@ -546,6 +653,34 @@ function encodeTransaction(config, chainId) {
|
|
|
546
653
|
value: config.value?.toString() ?? "0"
|
|
547
654
|
};
|
|
548
655
|
}
|
|
656
|
+
async function getMessageIndicesFromTx(params) {
|
|
657
|
+
const publicClient = getPublicClient({
|
|
658
|
+
chainId: params.chainId,
|
|
659
|
+
rpcUrl: params.rpcUrl
|
|
660
|
+
});
|
|
661
|
+
const netContract = getNetContract(params.chainId);
|
|
662
|
+
const contractAddress = netContract.address.toLowerCase();
|
|
663
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
664
|
+
hash: params.txHash
|
|
665
|
+
});
|
|
666
|
+
const indices = [];
|
|
667
|
+
for (const log of receipt.logs) {
|
|
668
|
+
if (log.address.toLowerCase() !== contractAddress) continue;
|
|
669
|
+
try {
|
|
670
|
+
const decoded = decodeEventLog({
|
|
671
|
+
abi: netContract.abi,
|
|
672
|
+
data: log.data,
|
|
673
|
+
topics: log.topics
|
|
674
|
+
});
|
|
675
|
+
if (decoded.eventName === "MessageSent") {
|
|
676
|
+
const args = decoded.args;
|
|
677
|
+
indices.push(Number(args.messageIndex));
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return indices;
|
|
683
|
+
}
|
|
549
684
|
|
|
550
685
|
// src/commands/feed/post.ts
|
|
551
686
|
var MAX_MESSAGE_LENGTH = 4e3;
|
|
@@ -597,11 +732,23 @@ ${options.body}` : message;
|
|
|
597
732
|
commonOptions.chainId,
|
|
598
733
|
commonOptions.rpcUrl
|
|
599
734
|
);
|
|
600
|
-
|
|
735
|
+
if (!options.json) {
|
|
736
|
+
console.log(chalk12.blue(`Posting to feed "${normalizedFeed}"...`));
|
|
737
|
+
}
|
|
601
738
|
try {
|
|
602
739
|
const hash = await executeTransaction(walletClient, txConfig);
|
|
603
740
|
const senderAddress = walletClient.account.address;
|
|
604
741
|
let postId;
|
|
742
|
+
let globalIndex;
|
|
743
|
+
try {
|
|
744
|
+
const indices = await getMessageIndicesFromTx({
|
|
745
|
+
chainId: commonOptions.chainId,
|
|
746
|
+
rpcUrl: commonOptions.rpcUrl,
|
|
747
|
+
txHash: hash
|
|
748
|
+
});
|
|
749
|
+
globalIndex = indices[0];
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
605
752
|
try {
|
|
606
753
|
const posts = await client.getFeedPosts({
|
|
607
754
|
topic: normalizedFeed,
|
|
@@ -623,19 +770,36 @@ ${options.body}` : message;
|
|
|
623
770
|
sender: senderAddress,
|
|
624
771
|
text: fullMessage,
|
|
625
772
|
postId
|
|
626
|
-
// Now we have the actual post ID for checking comments later
|
|
627
773
|
});
|
|
774
|
+
const permalink = postPermalink(commonOptions.chainId, {
|
|
775
|
+
globalIndex
|
|
776
|
+
});
|
|
777
|
+
if (options.json) {
|
|
778
|
+
printJson({
|
|
779
|
+
success: true,
|
|
780
|
+
txHash: hash,
|
|
781
|
+
explorerTxUrl: explorerTxUrl(commonOptions.chainId, hash),
|
|
782
|
+
postId,
|
|
783
|
+
globalIndex,
|
|
784
|
+
permalink,
|
|
785
|
+
feed: normalizedFeed,
|
|
786
|
+
feedUrl: feedUrl(commonOptions.chainId, normalizedFeed),
|
|
787
|
+
sender: senderAddress,
|
|
788
|
+
senderProfileUrl: profileUrl(commonOptions.chainId, senderAddress),
|
|
789
|
+
text: fullMessage
|
|
790
|
+
});
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
628
793
|
const displayText = options.body ? `${message} (+ body)` : message;
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
Text: ${displayText}`
|
|
637
|
-
|
|
638
|
-
);
|
|
794
|
+
const lines = [
|
|
795
|
+
`Message posted successfully!`,
|
|
796
|
+
` Transaction: ${hash}`,
|
|
797
|
+
` Feed: ${normalizedFeed}`
|
|
798
|
+
];
|
|
799
|
+
if (postId) lines.push(` Post ID: ${postId}`);
|
|
800
|
+
if (permalink) lines.push(` Permalink: ${permalink}`);
|
|
801
|
+
lines.push(` Text: ${displayText}`);
|
|
802
|
+
console.log(chalk12.green(lines.join("\n")));
|
|
639
803
|
} catch (error) {
|
|
640
804
|
exitWithError(
|
|
641
805
|
`Failed to post message: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -650,7 +814,10 @@ function registerFeedPostCommand(parent) {
|
|
|
650
814
|
).option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option(
|
|
651
815
|
"--encode-only",
|
|
652
816
|
"Output transaction data as JSON instead of executing"
|
|
653
|
-
).option("--data <data>", "Optional data to attach to the post").option("--body <text>", "Post body (message becomes the title)").
|
|
817
|
+
).option("--data <data>", "Optional data to attach to the post").option("--body <text>", "Post body (message becomes the title)").option(
|
|
818
|
+
"--json",
|
|
819
|
+
"Output structured JSON (includes permalink and other URLs) after submission"
|
|
820
|
+
).action(async (feed, message, options) => {
|
|
654
821
|
await executeFeedPost(feed, message, options);
|
|
655
822
|
});
|
|
656
823
|
}
|
|
@@ -679,11 +846,6 @@ function parsePostId(postId) {
|
|
|
679
846
|
timestamp
|
|
680
847
|
};
|
|
681
848
|
}
|
|
682
|
-
function findPostByParsedId(posts, parsedId) {
|
|
683
|
-
return posts.find(
|
|
684
|
-
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
849
|
|
|
688
850
|
// src/commands/feed/comment-write.ts
|
|
689
851
|
var MAX_MESSAGE_LENGTH2 = 4e3;
|
|
@@ -716,17 +878,20 @@ async function executeFeedCommentWrite(feed, postId, message, options) {
|
|
|
716
878
|
`Feed "${normalizedFeed}" has no posts. Cannot find post ${postId}.`
|
|
717
879
|
);
|
|
718
880
|
}
|
|
719
|
-
const
|
|
881
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
720
882
|
topic: normalizedFeed,
|
|
721
883
|
maxPosts: 100
|
|
722
|
-
// Fetch enough to find the post
|
|
723
884
|
});
|
|
724
|
-
const
|
|
885
|
+
const matchOffset = fetched.messages.findIndex(
|
|
886
|
+
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
887
|
+
);
|
|
888
|
+
const targetPost = matchOffset >= 0 ? fetched.messages[matchOffset] : void 0;
|
|
725
889
|
if (!targetPost) {
|
|
726
890
|
exitWithError(
|
|
727
891
|
`Post not found with ID ${postId} in feed "${normalizedFeed}". Make sure the sender and timestamp are correct.`
|
|
728
892
|
);
|
|
729
893
|
}
|
|
894
|
+
const parentTopicIndex = fetched.startIndex + matchOffset;
|
|
730
895
|
const txConfig = client.prepareComment({
|
|
731
896
|
post: targetPost,
|
|
732
897
|
text: message
|
|
@@ -750,26 +915,81 @@ async function executeFeedCommentWrite(feed, postId, message, options) {
|
|
|
750
915
|
commonOptions.chainId,
|
|
751
916
|
commonOptions.rpcUrl
|
|
752
917
|
);
|
|
753
|
-
|
|
918
|
+
if (!options.json) {
|
|
919
|
+
console.log(chalk12.blue(`Commenting on post ${postId}...`));
|
|
920
|
+
}
|
|
754
921
|
try {
|
|
755
922
|
const hash = await executeTransaction(walletClient, txConfig);
|
|
923
|
+
const senderAddress = walletClient.account.address;
|
|
924
|
+
let globalIndex;
|
|
925
|
+
try {
|
|
926
|
+
const indices = await getMessageIndicesFromTx({
|
|
927
|
+
chainId: commonOptions.chainId,
|
|
928
|
+
rpcUrl: commonOptions.rpcUrl,
|
|
929
|
+
txHash: hash
|
|
930
|
+
});
|
|
931
|
+
globalIndex = indices[0];
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
let commentTimestamp;
|
|
935
|
+
if (globalIndex !== void 0) {
|
|
936
|
+
try {
|
|
937
|
+
const netClient = createNetClient(commonOptions);
|
|
938
|
+
const fetchedComment = await netClient.getMessageAtIndex({
|
|
939
|
+
messageIndex: globalIndex
|
|
940
|
+
});
|
|
941
|
+
if (fetchedComment) {
|
|
942
|
+
commentTimestamp = Number(fetchedComment.timestamp);
|
|
943
|
+
}
|
|
944
|
+
} catch {
|
|
945
|
+
}
|
|
946
|
+
}
|
|
756
947
|
addHistoryEntry({
|
|
757
948
|
type: "comment",
|
|
758
949
|
txHash: hash,
|
|
759
950
|
chainId: commonOptions.chainId,
|
|
760
951
|
feed: normalizedFeed,
|
|
761
|
-
sender:
|
|
952
|
+
sender: senderAddress,
|
|
762
953
|
text: message,
|
|
763
954
|
postId
|
|
764
955
|
});
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
956
|
+
const parentPostUrl = postPermalink(commonOptions.chainId, {
|
|
957
|
+
topic: normalizedFeed,
|
|
958
|
+
topicIndex: parentTopicIndex
|
|
959
|
+
});
|
|
960
|
+
const commentPermalink = commentTimestamp !== void 0 ? postPermalink(commonOptions.chainId, {
|
|
961
|
+
topic: normalizedFeed,
|
|
962
|
+
topicIndex: parentTopicIndex,
|
|
963
|
+
commentId: `${senderAddress}-${commentTimestamp}`
|
|
964
|
+
}) : postPermalink(commonOptions.chainId, { globalIndex });
|
|
965
|
+
if (options.json) {
|
|
966
|
+
printJson({
|
|
967
|
+
success: true,
|
|
968
|
+
txHash: hash,
|
|
969
|
+
explorerTxUrl: explorerTxUrl(commonOptions.chainId, hash),
|
|
970
|
+
globalIndex,
|
|
971
|
+
permalink: commentPermalink,
|
|
972
|
+
parentPostId: postId,
|
|
973
|
+
parentPostUrl,
|
|
974
|
+
feed: normalizedFeed,
|
|
975
|
+
feedUrl: feedUrl(commonOptions.chainId, normalizedFeed),
|
|
976
|
+
sender: senderAddress,
|
|
977
|
+
senderProfileUrl: profileUrl(commonOptions.chainId, senderAddress),
|
|
978
|
+
text: message,
|
|
979
|
+
...commentTimestamp !== void 0 && {
|
|
980
|
+
commentId: `${senderAddress}:${commentTimestamp}`
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const lines = [
|
|
986
|
+
`Comment posted successfully!`,
|
|
987
|
+
` Transaction: ${hash}`,
|
|
988
|
+
` Post: ${postId}`
|
|
989
|
+
];
|
|
990
|
+
if (commentPermalink) lines.push(` Permalink: ${commentPermalink}`);
|
|
991
|
+
lines.push(` Comment: ${message}`);
|
|
992
|
+
console.log(chalk12.green(lines.join("\n")));
|
|
773
993
|
} catch (error) {
|
|
774
994
|
exitWithError(
|
|
775
995
|
`Failed to post comment: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -786,6 +1006,9 @@ function registerFeedCommentWriteCommand(parent) {
|
|
|
786
1006
|
).option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option(
|
|
787
1007
|
"--encode-only",
|
|
788
1008
|
"Output transaction data as JSON instead of executing"
|
|
1009
|
+
).option(
|
|
1010
|
+
"--json",
|
|
1011
|
+
"Output structured JSON (includes permalink and other URLs) after submission"
|
|
789
1012
|
).action(async (feed, postId, message, options) => {
|
|
790
1013
|
await executeFeedCommentWrite(feed, postId, message, options);
|
|
791
1014
|
});
|
|
@@ -812,17 +1035,25 @@ async function executeFeedCommentRead(feed, postId, options) {
|
|
|
812
1035
|
`Feed "${normalizedFeed}" has no posts. Cannot find post ${postId}.`
|
|
813
1036
|
);
|
|
814
1037
|
}
|
|
815
|
-
const
|
|
1038
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
816
1039
|
topic: normalizedFeed,
|
|
817
1040
|
maxPosts: 100
|
|
818
1041
|
// Fetch enough to find the post
|
|
819
1042
|
});
|
|
820
|
-
const
|
|
1043
|
+
const matchIndex = fetched.messages.findIndex(
|
|
1044
|
+
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
1045
|
+
);
|
|
1046
|
+
const targetPost = matchIndex >= 0 ? fetched.messages[matchIndex] : void 0;
|
|
821
1047
|
if (!targetPost) {
|
|
822
1048
|
exitWithError(
|
|
823
1049
|
`Post not found with ID ${postId} in feed "${normalizedFeed}". Make sure the sender and timestamp are correct.`
|
|
824
1050
|
);
|
|
825
1051
|
}
|
|
1052
|
+
const parentTopicIndex = fetched.startIndex + matchIndex;
|
|
1053
|
+
const parentPostUrl = postPermalink(readOnlyOptions.chainId, {
|
|
1054
|
+
topic: normalizedFeed,
|
|
1055
|
+
topicIndex: parentTopicIndex
|
|
1056
|
+
});
|
|
826
1057
|
const commentCount = await client.getCommentCount(targetPost);
|
|
827
1058
|
if (commentCount === 0) {
|
|
828
1059
|
if (options.json) {
|
|
@@ -843,7 +1074,11 @@ async function executeFeedCommentRead(feed, postId, options) {
|
|
|
843
1074
|
if (options.json) {
|
|
844
1075
|
printJson(
|
|
845
1076
|
commentsWithDepth.map(
|
|
846
|
-
({ comment, depth }) => commentToJson(comment,
|
|
1077
|
+
({ comment, depth }) => commentToJson(comment, {
|
|
1078
|
+
chainId: readOnlyOptions.chainId,
|
|
1079
|
+
depth,
|
|
1080
|
+
parentPostUrl
|
|
1081
|
+
})
|
|
847
1082
|
)
|
|
848
1083
|
);
|
|
849
1084
|
} else {
|
|
@@ -984,8 +1219,10 @@ async function executeFeedReplies(options) {
|
|
|
984
1219
|
rpcUrl: options.rpcUrl
|
|
985
1220
|
});
|
|
986
1221
|
const client = createFeedClient(readOnlyOptions);
|
|
987
|
-
|
|
1222
|
+
if (!options.json) {
|
|
1223
|
+
console.log(chalk12.blue(`Checking replies on ${postsWithIds.length} posts...
|
|
988
1224
|
`));
|
|
1225
|
+
}
|
|
989
1226
|
const results = [];
|
|
990
1227
|
for (const entry of postsWithIds) {
|
|
991
1228
|
try {
|
|
@@ -1000,12 +1237,31 @@ async function executeFeedReplies(options) {
|
|
|
1000
1237
|
data: "0x"
|
|
1001
1238
|
};
|
|
1002
1239
|
const commentCount = await client.getCommentCount(postObj);
|
|
1240
|
+
let permalink = null;
|
|
1241
|
+
try {
|
|
1242
|
+
const indices = await getMessageIndicesFromTx({
|
|
1243
|
+
chainId: readOnlyOptions.chainId,
|
|
1244
|
+
rpcUrl: readOnlyOptions.rpcUrl,
|
|
1245
|
+
txHash: entry.txHash
|
|
1246
|
+
});
|
|
1247
|
+
if (indices[0] !== void 0) {
|
|
1248
|
+
permalink = postPermalink(readOnlyOptions.chainId, {
|
|
1249
|
+
globalIndex: indices[0]
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
} catch {
|
|
1253
|
+
}
|
|
1003
1254
|
results.push({
|
|
1004
1255
|
feed: entry.feed,
|
|
1005
1256
|
postId: entry.postId,
|
|
1006
1257
|
text: entry.text ?? "",
|
|
1007
1258
|
postedAt: entry.timestamp,
|
|
1008
|
-
commentCount: Number(commentCount)
|
|
1259
|
+
commentCount: Number(commentCount),
|
|
1260
|
+
permalink,
|
|
1261
|
+
feedUrl: feedUrl(readOnlyOptions.chainId, entry.feed),
|
|
1262
|
+
senderProfileUrl: entry.sender ? profileUrl(readOnlyOptions.chainId, entry.sender) : null,
|
|
1263
|
+
explorerTxUrl: explorerTxUrl(readOnlyOptions.chainId, entry.txHash),
|
|
1264
|
+
txHash: entry.txHash
|
|
1009
1265
|
});
|
|
1010
1266
|
} catch {
|
|
1011
1267
|
}
|
|
@@ -1090,7 +1346,14 @@ async function executeFeedPosts(address, options) {
|
|
|
1090
1346
|
endIndex: count
|
|
1091
1347
|
});
|
|
1092
1348
|
if (options.json) {
|
|
1093
|
-
printJson(
|
|
1349
|
+
printJson(
|
|
1350
|
+
messages.map(
|
|
1351
|
+
(msg, i) => postToJson(msg, {
|
|
1352
|
+
chainId: readOnlyOptions.chainId,
|
|
1353
|
+
userIndex: startIndex + i
|
|
1354
|
+
})
|
|
1355
|
+
)
|
|
1356
|
+
);
|
|
1094
1357
|
} else {
|
|
1095
1358
|
console.log(
|
|
1096
1359
|
chalk12.white(`Found ${messages.length} post(s) by ${address}:
|
|
@@ -1268,11 +1531,14 @@ function historyEntryToJson(entry, index) {
|
|
|
1268
1531
|
type: entry.type,
|
|
1269
1532
|
timestamp: entry.timestamp,
|
|
1270
1533
|
txHash: entry.txHash,
|
|
1534
|
+
explorerTxUrl: explorerTxUrl(entry.chainId, entry.txHash),
|
|
1271
1535
|
chainId: entry.chainId,
|
|
1272
|
-
feed: entry.feed
|
|
1536
|
+
feed: entry.feed,
|
|
1537
|
+
feedUrl: feedUrl(entry.chainId, entry.feed)
|
|
1273
1538
|
};
|
|
1274
1539
|
if (entry.sender) {
|
|
1275
1540
|
result.sender = entry.sender;
|
|
1541
|
+
result.senderProfileUrl = profileUrl(entry.chainId, entry.sender);
|
|
1276
1542
|
}
|
|
1277
1543
|
if (entry.text) {
|
|
1278
1544
|
result.text = entry.text;
|
|
@@ -1446,7 +1712,9 @@ async function executeListAgents(options) {
|
|
|
1446
1712
|
if (options.json) {
|
|
1447
1713
|
printJson({
|
|
1448
1714
|
totalCount,
|
|
1449
|
-
agents: agents.map(
|
|
1715
|
+
agents: agents.map(
|
|
1716
|
+
(agent, i) => agentToJson(agent, i, readOnlyOptions.chainId)
|
|
1717
|
+
)
|
|
1450
1718
|
});
|
|
1451
1719
|
} else {
|
|
1452
1720
|
if (agents.length === 0) {
|
|
@@ -1505,9 +1773,13 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
1505
1773
|
const netClient = createNetClient(readOnlyOptions);
|
|
1506
1774
|
const existingHistory = getHistory();
|
|
1507
1775
|
if (existingHistory.some((entry) => entry.txHash === txHash)) {
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1776
|
+
if (options.json) {
|
|
1777
|
+
printJson({ alreadyRecorded: true, txHash });
|
|
1778
|
+
} else {
|
|
1779
|
+
console.log(
|
|
1780
|
+
chalk12.yellow("Transaction already recorded in history. Skipping.")
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1511
1783
|
return;
|
|
1512
1784
|
}
|
|
1513
1785
|
const receipt = await publicClient.getTransactionReceipt({ hash: txHash }).catch(
|
|
@@ -1542,11 +1814,13 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
1542
1814
|
"Transaction does not contain any Net protocol messages."
|
|
1543
1815
|
);
|
|
1544
1816
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1817
|
+
if (!options.json) {
|
|
1818
|
+
console.log(
|
|
1819
|
+
chalk12.blue(
|
|
1820
|
+
`Found ${messageSentEvents.length} message(s) in transaction. Fetching details...`
|
|
1821
|
+
)
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1550
1824
|
const messages = await Promise.all(
|
|
1551
1825
|
messageSentEvents.map(
|
|
1552
1826
|
(event) => netClient.getMessageAtIndex({
|
|
@@ -1554,41 +1828,40 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
1554
1828
|
})
|
|
1555
1829
|
)
|
|
1556
1830
|
);
|
|
1831
|
+
const entries = [];
|
|
1557
1832
|
let recorded = 0;
|
|
1558
1833
|
for (let i = 0; i < messages.length; i++) {
|
|
1559
1834
|
const message = messages[i];
|
|
1560
1835
|
if (!message) {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1836
|
+
if (!options.json) {
|
|
1837
|
+
console.log(
|
|
1838
|
+
chalk12.yellow(
|
|
1839
|
+
` Could not fetch message at index ${messageSentEvents[i].messageIndex}. Skipping.`
|
|
1840
|
+
)
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1566
1843
|
continue;
|
|
1567
1844
|
}
|
|
1845
|
+
const globalIndex = Number(messageSentEvents[i].messageIndex);
|
|
1568
1846
|
const feedName = extractFeedName(message.topic);
|
|
1569
1847
|
const isComment = isCommentTopic(message.topic);
|
|
1570
1848
|
let type;
|
|
1571
1849
|
let postId;
|
|
1572
|
-
let
|
|
1850
|
+
let parentPostId;
|
|
1851
|
+
let permalink = null;
|
|
1573
1852
|
if (isComment) {
|
|
1574
1853
|
type = "comment";
|
|
1575
1854
|
const commentData = parseCommentData(message.data);
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
Tx: ${txHash}`;
|
|
1855
|
+
parentPostId = commentData ? `${commentData.parentSender}:${commentData.parentTimestamp}` : void 0;
|
|
1856
|
+
const commentParam = `${message.sender}-${Number(message.timestamp)}`;
|
|
1857
|
+
permalink = postPermalink(readOnlyOptions.chainId, {
|
|
1858
|
+
globalIndex,
|
|
1859
|
+
commentId: commentParam
|
|
1860
|
+
});
|
|
1583
1861
|
} else {
|
|
1584
1862
|
type = "post";
|
|
1585
1863
|
postId = createPostId(message);
|
|
1586
|
-
|
|
1587
|
-
Feed: ${feedName}
|
|
1588
|
-
Sender: ${message.sender}
|
|
1589
|
-
Text: ${message.text}
|
|
1590
|
-
Post ID: ${postId}
|
|
1591
|
-
Tx: ${txHash}`;
|
|
1864
|
+
permalink = postPermalink(readOnlyOptions.chainId, { globalIndex });
|
|
1592
1865
|
}
|
|
1593
1866
|
addHistoryEntry({
|
|
1594
1867
|
type,
|
|
@@ -1597,11 +1870,49 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
1597
1870
|
feed: feedName,
|
|
1598
1871
|
sender: message.sender,
|
|
1599
1872
|
text: message.text,
|
|
1600
|
-
postId
|
|
1873
|
+
postId: type === "comment" ? parentPostId : postId
|
|
1601
1874
|
});
|
|
1602
|
-
|
|
1875
|
+
const entry = {
|
|
1876
|
+
type,
|
|
1877
|
+
txHash,
|
|
1878
|
+
explorerTxUrl: explorerTxUrl(readOnlyOptions.chainId, txHash),
|
|
1879
|
+
globalIndex,
|
|
1880
|
+
permalink,
|
|
1881
|
+
feed: feedName,
|
|
1882
|
+
feedUrl: feedUrl(readOnlyOptions.chainId, feedName),
|
|
1883
|
+
sender: message.sender,
|
|
1884
|
+
senderProfileUrl: profileUrl(
|
|
1885
|
+
readOnlyOptions.chainId,
|
|
1886
|
+
message.sender
|
|
1887
|
+
),
|
|
1888
|
+
text: message.text,
|
|
1889
|
+
timestamp: Number(message.timestamp)
|
|
1890
|
+
};
|
|
1891
|
+
if (type === "post") entry.postId = postId;
|
|
1892
|
+
if (type === "comment") entry.parentPostId = parentPostId;
|
|
1893
|
+
entries.push(entry);
|
|
1894
|
+
if (!options.json) {
|
|
1895
|
+
const label = type === "comment" ? `Verified comment:
|
|
1896
|
+
Feed: ${feedName}
|
|
1897
|
+
Sender: ${message.sender}
|
|
1898
|
+
Text: ${message.text}
|
|
1899
|
+
Parent post: ${parentPostId ?? "unknown"}
|
|
1900
|
+
Permalink: ${permalink ?? "(unavailable)"}
|
|
1901
|
+
Tx: ${txHash}` : `Verified post:
|
|
1902
|
+
Feed: ${feedName}
|
|
1903
|
+
Sender: ${message.sender}
|
|
1904
|
+
Text: ${message.text}
|
|
1905
|
+
Post ID: ${postId}
|
|
1906
|
+
Permalink: ${permalink ?? "(unavailable)"}
|
|
1907
|
+
Tx: ${txHash}`;
|
|
1908
|
+
console.log(chalk12.green(` ${label}`));
|
|
1909
|
+
}
|
|
1603
1910
|
recorded++;
|
|
1604
1911
|
}
|
|
1912
|
+
if (options.json) {
|
|
1913
|
+
printJson({ recorded, entries });
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1605
1916
|
if (recorded > 0) {
|
|
1606
1917
|
console.log(
|
|
1607
1918
|
chalk12.green(`
|
|
@@ -1616,7 +1927,10 @@ function registerFeedVerifyClaimCommand(parent) {
|
|
|
1616
1927
|
"--chain-id <id>",
|
|
1617
1928
|
"Chain ID (default: 8453 for Base)",
|
|
1618
1929
|
(value) => parseInt(value, 10)
|
|
1619
|
-
).option("--rpc-url <url>", "Custom RPC URL").
|
|
1930
|
+
).option("--rpc-url <url>", "Custom RPC URL").option(
|
|
1931
|
+
"--json",
|
|
1932
|
+
"Output structured JSON (includes permalink and other URLs)"
|
|
1933
|
+
).action(async (txHash, options) => {
|
|
1620
1934
|
await executeFeedVerifyClaim(txHash, options);
|
|
1621
1935
|
});
|
|
1622
1936
|
}
|