@skills-store/rednote 0.1.14 → 0.1.15
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 +19 -327
- package/dist/index.js +2 -2
- package/dist/rednote/index.js +2 -2
- package/dist/rednote/interact.js +30 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,10 +36,10 @@ For most tasks, run commands in this order:
|
|
|
36
36
|
rednote env
|
|
37
37
|
rednote browser create --name seller-main --browser chrome --port 9222
|
|
38
38
|
rednote browser connect --instance seller-main
|
|
39
|
-
rednote login
|
|
40
|
-
rednote status
|
|
41
|
-
rednote search --
|
|
42
|
-
rednote interact --
|
|
39
|
+
rednote login
|
|
40
|
+
rednote status
|
|
41
|
+
rednote search --keyword 护肤
|
|
42
|
+
rednote interact --url "https://www.xiaohongshu.com/explore/xxx?xsec_token=yyy" --like --collect --comment "写得真好"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Commands
|
|
@@ -68,7 +68,7 @@ Use `env` first when checking installation, runtime info, or storage paths.
|
|
|
68
68
|
### `status`
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
rednote status
|
|
71
|
+
rednote status
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Use `status` to confirm whether an instance exists, is running, and appears logged in.
|
|
@@ -76,7 +76,7 @@ Use `status` to confirm whether an instance exists, is running, and appears logg
|
|
|
76
76
|
### `check-login`
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
rednote check-login
|
|
79
|
+
rednote check-login
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Use `check-login` when you only want to verify whether the session is still valid.
|
|
@@ -84,7 +84,7 @@ Use `check-login` when you only want to verify whether the session is still vali
|
|
|
84
84
|
### `login`
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
rednote login
|
|
87
|
+
rednote login
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
Use `login` after `browser connect` if the instance is not authenticated yet.
|
|
@@ -92,7 +92,7 @@ Use `login` after `browser connect` if the instance is not authenticated yet.
|
|
|
92
92
|
### `home`
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
|
-
rednote home --
|
|
95
|
+
rednote home --format md --save
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
Use `home` when you want the current home feed and optionally want to save it to disk.
|
|
@@ -103,6 +103,10 @@ The terminal output always uses the compact summary format below, even when `--f
|
|
|
103
103
|
id=<database nanoid>
|
|
104
104
|
title=<post title>
|
|
105
105
|
like=<liked count>
|
|
106
|
+
|
|
107
|
+
id=...
|
|
108
|
+
title=...
|
|
109
|
+
like=...
|
|
106
110
|
```
|
|
107
111
|
|
|
108
112
|
Captured home feed posts are upserted into `~/.skills-router/rednote/main.db` (the same path returned by `rednote env`). They are stored in the `rednote_posts` table, and the printed `id` is that table's `nanoid(16)` primary key.
|
|
@@ -110,8 +114,8 @@ Captured home feed posts are upserted into `~/.skills-router/rednote/main.db` (t
|
|
|
110
114
|
### `search`
|
|
111
115
|
|
|
112
116
|
```bash
|
|
113
|
-
rednote search --
|
|
114
|
-
rednote search --
|
|
117
|
+
rednote search --keyword 护肤
|
|
118
|
+
rednote search --keyword 护肤 --format json --save ./output/search.jsonl
|
|
115
119
|
```
|
|
116
120
|
|
|
117
121
|
Use `search` for keyword-based note lookup.
|
|
@@ -129,8 +133,8 @@ Captured search results are also upserted into `~/.skills-router/rednote/main.db
|
|
|
129
133
|
### `get-feed-detail`
|
|
130
134
|
|
|
131
135
|
```bash
|
|
132
|
-
rednote get-feed-detail --
|
|
133
|
-
rednote get-feed-detail --
|
|
136
|
+
rednote get-feed-detail --id <nanoid>
|
|
137
|
+
rednote get-feed-detail --url "https://www.xiaohongshu.com/explore/xxx?xsec_token=yyy"
|
|
134
138
|
```
|
|
135
139
|
|
|
136
140
|
Use `get-feed-detail` when you already have a Xiaohongshu note URL, or when you have a database `id` returned by `home` or `search`. With `--id`, the CLI looks up the saved URL from `~/.skills-router/rednote/main.db` and then navigates with that raw URL.
|
|
@@ -140,7 +144,7 @@ Captured note details and comments are also upserted into `~/.skills-router/redn
|
|
|
140
144
|
### `get-profile`
|
|
141
145
|
|
|
142
146
|
```bash
|
|
143
|
-
rednote get-profile --
|
|
147
|
+
rednote get-profile --id USER_ID
|
|
144
148
|
```
|
|
145
149
|
|
|
146
150
|
Use `get-profile` when you want author or account profile information.
|
|
@@ -149,8 +153,8 @@ Use `get-profile` when you want author or account profile information.
|
|
|
149
153
|
### `interact`
|
|
150
154
|
|
|
151
155
|
```bash
|
|
152
|
-
rednote interact --
|
|
153
|
-
rednote interact --
|
|
156
|
+
rednote interact --id <nanoid> --like --collect
|
|
157
|
+
rednote interact --id <nanoid> --like --collect --comment "写得真好"
|
|
154
158
|
```
|
|
155
159
|
|
|
156
160
|
Use `interact` when you want the single entrypoint for note operations such as like, collect, and comment in one command. Use `--comment TEXT` for replies; there is no standalone `comment` command.
|
|
@@ -168,318 +172,6 @@ Use `interact` when you want the single entrypoint for note operations such as l
|
|
|
168
172
|
- `--url` is required for `interact`; at least one of `--like`, `--collect`, or `--comment TEXT` must be provided.
|
|
169
173
|
- replies are sent with `interact --comment TEXT`.
|
|
170
174
|
|
|
171
|
-
## JSON success shapes
|
|
172
|
-
|
|
173
|
-
Use these shapes as the success model when a command returns JSON.
|
|
174
|
-
|
|
175
|
-
### Shared note item
|
|
176
|
-
|
|
177
|
-
`home`, `search`, and `profile.notes` share the normalized `RednotePost` shape:
|
|
178
|
-
|
|
179
|
-
```json
|
|
180
|
-
{
|
|
181
|
-
"id": "string",
|
|
182
|
-
"modelType": "string",
|
|
183
|
-
"xsecToken": "string|null",
|
|
184
|
-
"url": "string",
|
|
185
|
-
"noteCard": {
|
|
186
|
-
"type": "string|null",
|
|
187
|
-
"displayTitle": "string|null",
|
|
188
|
-
"cover": {
|
|
189
|
-
"urlDefault": "string|null",
|
|
190
|
-
"urlPre": "string|null",
|
|
191
|
-
"url": "string|null",
|
|
192
|
-
"fileId": "string|null",
|
|
193
|
-
"width": "number|null",
|
|
194
|
-
"height": "number|null",
|
|
195
|
-
"infoList": [{ "imageScene": "string|null", "url": "string|null" }]
|
|
196
|
-
},
|
|
197
|
-
"user": {
|
|
198
|
-
"userId": "string|null",
|
|
199
|
-
"nickname": "string|null",
|
|
200
|
-
"nickName": "string|null",
|
|
201
|
-
"avatar": "string|null",
|
|
202
|
-
"xsecToken": "string|null"
|
|
203
|
-
},
|
|
204
|
-
"interactInfo": {
|
|
205
|
-
"liked": "boolean",
|
|
206
|
-
"likedCount": "string|null",
|
|
207
|
-
"commentCount": "string|null",
|
|
208
|
-
"collectedCount": "string|null",
|
|
209
|
-
"sharedCount": "string|null"
|
|
210
|
-
},
|
|
211
|
-
"cornerTagInfo": [{ "type": "string|null", "text": "string|null" }],
|
|
212
|
-
"imageList": [{ "width": "number|null", "height": "number|null", "infoList": [{ "imageScene": "string|null", "url": "string|null" }] }],
|
|
213
|
-
"video": { "duration": "number|null" }
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### `env --format json`
|
|
219
|
-
|
|
220
|
-
`env` is the main exception: it returns a raw environment object instead of `{ "ok": true, ... }`.
|
|
221
|
-
|
|
222
|
-
```json
|
|
223
|
-
{
|
|
224
|
-
"packageRoot": "string",
|
|
225
|
-
"homeDir": "string",
|
|
226
|
-
"platform": "string",
|
|
227
|
-
"nodeVersion": "string",
|
|
228
|
-
"storageHome": "string",
|
|
229
|
-
"storageRoot": "string",
|
|
230
|
-
"databasePath": "string",
|
|
231
|
-
"instancesDir": "string",
|
|
232
|
-
"instanceStorePath": "string",
|
|
233
|
-
"legacyPackageInstancesDir": "string"
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Browser commands
|
|
238
|
-
|
|
239
|
-
`browser list`:
|
|
240
|
-
|
|
241
|
-
```json
|
|
242
|
-
{
|
|
243
|
-
"lastConnect": { "scope": "default|custom", "name": "string", "browser": "chrome|edge|chromium|brave" } | null,
|
|
244
|
-
"instances": [{
|
|
245
|
-
"type": "chrome|edge|chromium|brave",
|
|
246
|
-
"name": "string",
|
|
247
|
-
"executablePath": "string",
|
|
248
|
-
"userDataDir": "string",
|
|
249
|
-
"exists": true,
|
|
250
|
-
"inUse": false,
|
|
251
|
-
"pid": "number|null",
|
|
252
|
-
"lockFiles": ["string"],
|
|
253
|
-
"matchedProcess": { "pid": "number", "name": "string", "cmdline": "string" } | null,
|
|
254
|
-
"staleLock": false,
|
|
255
|
-
"remotePort": "number|null",
|
|
256
|
-
"scope": "default|custom",
|
|
257
|
-
"instanceName": "string",
|
|
258
|
-
"createdAt": "string|null",
|
|
259
|
-
"lastConnect": false
|
|
260
|
-
}]
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
`browser create`:
|
|
265
|
-
|
|
266
|
-
```json
|
|
267
|
-
{
|
|
268
|
-
"ok": true,
|
|
269
|
-
"instance": {
|
|
270
|
-
"name": "string",
|
|
271
|
-
"browser": "chrome|edge|chromium|brave",
|
|
272
|
-
"userDataDir": "string",
|
|
273
|
-
"createdAt": "string",
|
|
274
|
-
"remoteDebuggingPort": "number|undefined"
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
`browser connect`:
|
|
280
|
-
|
|
281
|
-
```json
|
|
282
|
-
{
|
|
283
|
-
"ok": true,
|
|
284
|
-
"type": "chrome|edge|chromium|brave",
|
|
285
|
-
"executablePath": "string",
|
|
286
|
-
"userDataDir": "string",
|
|
287
|
-
"remoteDebuggingPort": "number",
|
|
288
|
-
"wsUrl": "string",
|
|
289
|
-
"pid": "number|null"
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
`browser remove`:
|
|
294
|
-
|
|
295
|
-
```json
|
|
296
|
-
{
|
|
297
|
-
"ok": true,
|
|
298
|
-
"removedInstance": {
|
|
299
|
-
"name": "string",
|
|
300
|
-
"browser": "chrome|edge|chromium|brave",
|
|
301
|
-
"userDataDir": "string",
|
|
302
|
-
"createdAt": "string",
|
|
303
|
-
"remoteDebuggingPort": "number|undefined"
|
|
304
|
-
},
|
|
305
|
-
"removedDir": true,
|
|
306
|
-
"closedPids": ["number"]
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Session and account commands
|
|
311
|
-
|
|
312
|
-
`status`:
|
|
313
|
-
|
|
314
|
-
```json
|
|
315
|
-
{
|
|
316
|
-
"ok": true,
|
|
317
|
-
"instance": {
|
|
318
|
-
"scope": "default|custom",
|
|
319
|
-
"name": "string",
|
|
320
|
-
"browser": "chrome|edge|chromium|brave",
|
|
321
|
-
"source": "argument|last-connect|single-instance",
|
|
322
|
-
"status": "running|stopped|missing|stale-lock",
|
|
323
|
-
"exists": true,
|
|
324
|
-
"inUse": false,
|
|
325
|
-
"pid": "number|null",
|
|
326
|
-
"remotePort": "number|null",
|
|
327
|
-
"userDataDir": "string",
|
|
328
|
-
"createdAt": "string|null",
|
|
329
|
-
"lastConnect": false
|
|
330
|
-
},
|
|
331
|
-
"rednote": {
|
|
332
|
-
"loginStatus": "logged-in|logged-out|unknown",
|
|
333
|
-
"lastLoginAt": "string|null"
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
`check-login`:
|
|
339
|
-
|
|
340
|
-
```json
|
|
341
|
-
{
|
|
342
|
-
"ok": true,
|
|
343
|
-
"rednote": {
|
|
344
|
-
"loginStatus": "logged-in|logged-out|unknown",
|
|
345
|
-
"lastLoginAt": "string|null",
|
|
346
|
-
"needLogin": false,
|
|
347
|
-
"checkedAt": "string"
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
`login`:
|
|
353
|
-
|
|
354
|
-
```json
|
|
355
|
-
{
|
|
356
|
-
"ok": true,
|
|
357
|
-
"rednote": {
|
|
358
|
-
"loginClicked": true,
|
|
359
|
-
"pageUrl": "string",
|
|
360
|
-
"waitingForPhoneLogin": true,
|
|
361
|
-
"qrCodePath": "string|null",
|
|
362
|
-
"message": "string"
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Feed and profile commands
|
|
368
|
-
|
|
369
|
-
`home` stdout (both `md` and `json`):
|
|
370
|
-
|
|
371
|
-
```text
|
|
372
|
-
id=<database nanoid>
|
|
373
|
-
title=<post title>
|
|
374
|
-
like=<liked count>
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
`home --format json --save PATH` writes the raw `RednotePost[]` array to disk, while stdout still prints the summary list above.
|
|
378
|
-
|
|
379
|
-
`search` stdout (both `md` and `json`):
|
|
380
|
-
|
|
381
|
-
```text
|
|
382
|
-
id=<database nanoid>
|
|
383
|
-
title=<post title>
|
|
384
|
-
like=<liked count>
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
`search --format json --save PATH` writes the raw `RednotePost[]` array to disk, while stdout still prints the summary list above.
|
|
388
|
-
|
|
389
|
-
`get-feed-detail --format json`:
|
|
390
|
-
|
|
391
|
-
```json
|
|
392
|
-
{
|
|
393
|
-
"ok": true,
|
|
394
|
-
"detail": {
|
|
395
|
-
"fetchedAt": "string",
|
|
396
|
-
"total": "number",
|
|
397
|
-
"items": [{
|
|
398
|
-
"url": "string",
|
|
399
|
-
"note": {
|
|
400
|
-
"noteId": "string|null",
|
|
401
|
-
"title": "string|null",
|
|
402
|
-
"desc": "string|null",
|
|
403
|
-
"type": "string|null",
|
|
404
|
-
"interactInfo": {
|
|
405
|
-
"liked": "boolean|null",
|
|
406
|
-
"likedCount": "string|null",
|
|
407
|
-
"commentCount": "string|null",
|
|
408
|
-
"collected": "boolean|null",
|
|
409
|
-
"collectedCount": "string|null",
|
|
410
|
-
"shareCount": "string|null",
|
|
411
|
-
"followed": "boolean|null"
|
|
412
|
-
},
|
|
413
|
-
"tagList": [{ "name": "string|null" }],
|
|
414
|
-
"imageList": [{ "urlDefault": "string|null", "urlPre": "string|null", "width": "number|null", "height": "number|null" }],
|
|
415
|
-
"video": { "url": "string|null", "raw": "unknown" } | null,
|
|
416
|
-
"raw": "unknown"
|
|
417
|
-
},
|
|
418
|
-
"comments": [{
|
|
419
|
-
"id": "string|null",
|
|
420
|
-
"content": "string|null",
|
|
421
|
-
"userId": "string|null",
|
|
422
|
-
"nickname": "string|null",
|
|
423
|
-
"likedCount": "string|null",
|
|
424
|
-
"subCommentCount": "number|null",
|
|
425
|
-
"raw": "unknown"
|
|
426
|
-
}]
|
|
427
|
-
}]
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
`get-profile --format json`:
|
|
433
|
-
|
|
434
|
-
```json
|
|
435
|
-
{
|
|
436
|
-
"ok": true,
|
|
437
|
-
"profile": {
|
|
438
|
-
"userId": "string",
|
|
439
|
-
"url": "string",
|
|
440
|
-
"fetchedAt": "string",
|
|
441
|
-
"user": {
|
|
442
|
-
"userId": "string|null",
|
|
443
|
-
"nickname": "string|null",
|
|
444
|
-
"desc": "string|null",
|
|
445
|
-
"avatar": "string|null",
|
|
446
|
-
"ipLocation": "string|null",
|
|
447
|
-
"gender": "string|null",
|
|
448
|
-
"follows": "string|number|null",
|
|
449
|
-
"fans": "string|number|null",
|
|
450
|
-
"interaction": "string|number|null",
|
|
451
|
-
"tags": ["string"],
|
|
452
|
-
"raw": "unknown"
|
|
453
|
-
},
|
|
454
|
-
"notes": ["RednotePost"],
|
|
455
|
-
"raw": {
|
|
456
|
-
"userPageData": "unknown",
|
|
457
|
-
"notes": "unknown"
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Action commands
|
|
464
|
-
|
|
465
|
-
`publish`:
|
|
466
|
-
|
|
467
|
-
```json
|
|
468
|
-
{
|
|
469
|
-
"ok": true,
|
|
470
|
-
"message": "string"
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
`interact`:
|
|
475
|
-
|
|
476
|
-
```json
|
|
477
|
-
{
|
|
478
|
-
"ok": true,
|
|
479
|
-
"message": "string"
|
|
480
|
-
}
|
|
481
|
-
```
|
|
482
|
-
|
|
483
175
|
## Storage
|
|
484
176
|
|
|
485
177
|
The CLI stores browser instances and metadata under:
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ Commands:
|
|
|
20
20
|
check-login [--instance NAME]
|
|
21
21
|
login [--instance NAME]
|
|
22
22
|
publish [--instance NAME]
|
|
23
|
-
interact [--instance NAME] --url URL [--like] [--collect] [--comment TEXT]
|
|
23
|
+
interact [--instance NAME] [--id ID | --url URL] [--like] [--collect] [--comment TEXT]
|
|
24
24
|
home [--instance NAME] [--format md|json] [--save [PATH]]
|
|
25
25
|
search [--instance NAME] --keyword TEXT [--format md|json] [--save [PATH]]
|
|
26
26
|
get-feed-detail [--instance NAME] --url URL [--format md|json]
|
|
@@ -32,7 +32,7 @@ Examples:
|
|
|
32
32
|
npx -y @skills-store/rednote browser connect --instance seller-main
|
|
33
33
|
npx -y @skills-store/rednote env
|
|
34
34
|
npx -y @skills-store/rednote publish --instance seller-main --type video --video ./note.mp4 --title 标题 --content 描述
|
|
35
|
-
npx -y @skills-store/rednote interact --instance seller-main --
|
|
35
|
+
npx -y @skills-store/rednote interact --instance seller-main --id NOTE_ID --like --collect --comment "写得真好"
|
|
36
36
|
npx -y @skills-store/rednote search --instance seller-main --keyword 护肤
|
|
37
37
|
`);
|
|
38
38
|
}
|
package/dist/rednote/index.js
CHANGED
|
@@ -11,7 +11,7 @@ Commands:
|
|
|
11
11
|
check-login [--instance NAME]
|
|
12
12
|
login [--instance NAME]
|
|
13
13
|
publish [--instance NAME]
|
|
14
|
-
interact [--instance NAME] --url URL [--like] [--collect] [--comment TEXT]
|
|
14
|
+
interact [--instance NAME] [--id ID | --url URL] [--like] [--collect] [--comment TEXT]
|
|
15
15
|
home [--instance NAME] [--format md|json] [--save [PATH]]
|
|
16
16
|
search [--instance NAME] --keyword TEXT [--format md|json] [--save [PATH]]
|
|
17
17
|
get-feed-detail [--instance NAME] --url URL [--url URL] [--comments [COUNT]] [--format md|json] [--save PATH]
|
|
@@ -25,7 +25,7 @@ Examples:
|
|
|
25
25
|
npx -y @skills-store/rednote status --instance seller-main
|
|
26
26
|
npx -y @skills-store/rednote login --instance seller-main
|
|
27
27
|
npx -y @skills-store/rednote publish --instance seller-main --type video --video ./note.mp4 --title "Video title" --content "Video description"
|
|
28
|
-
npx -y @skills-store/rednote interact --instance seller-main --
|
|
28
|
+
npx -y @skills-store/rednote interact --instance seller-main --id NOTE_ID --like --collect --comment "Great post"
|
|
29
29
|
npx -y @skills-store/rednote home --instance seller-main --format md --save
|
|
30
30
|
npx -y @skills-store/rednote search --instance seller-main --keyword skincare --format json --save ./output/search.json
|
|
31
31
|
npx -y @skills-store/rednote get-feed-detail --instance seller-main --url "https://www.xiaohongshu.com/explore/xxx?xsec_token=yyy" --comments 100 --format json --save ./output/feed-detail.json
|
package/dist/rednote/interact.js
CHANGED
|
@@ -4,6 +4,7 @@ import { printJson, runCli } from '../utils/browser-cli.js';
|
|
|
4
4
|
import { resolveStatusTarget } from './status.js';
|
|
5
5
|
import { createRednoteSession, disconnectRednoteSession, ensureRednoteLoggedIn } from './checkLogin.js';
|
|
6
6
|
import { getFeedDetails } from './getFeedDetail.js';
|
|
7
|
+
import { findPersistedPostUrlByRecordId, initializeRednoteDatabase } from './persistence.js';
|
|
7
8
|
const INTERACT_CONTAINER_SELECTOR = '.interact-container';
|
|
8
9
|
const LIKE_WRAPPER_SELECTOR = `${INTERACT_CONTAINER_SELECTOR} .like-wrapper`;
|
|
9
10
|
const COLLECT_WRAPPER_SELECTOR = `${INTERACT_CONTAINER_SELECTOR} .collect-wrapper, ${INTERACT_CONTAINER_SELECTOR} #note-page-collect-board-guide`;
|
|
@@ -14,13 +15,14 @@ function printInteractHelp() {
|
|
|
14
15
|
process.stdout.write(`rednote interact
|
|
15
16
|
|
|
16
17
|
Usage:
|
|
17
|
-
npx -y @skills-store/rednote interact [--instance NAME] --url URL [--like] [--collect] [--comment TEXT]
|
|
18
|
-
node --experimental-strip-types ./scripts/rednote/interact.ts --instance NAME --url URL [--like] [--collect] [--comment TEXT]
|
|
19
|
-
bun ./scripts/rednote/interact.ts --instance NAME --url URL [--like] [--collect] [--comment TEXT]
|
|
18
|
+
npx -y @skills-store/rednote interact [--instance NAME] [--id ID | --url URL] [--like] [--collect] [--comment TEXT]
|
|
19
|
+
node --experimental-strip-types ./scripts/rednote/interact.ts [--instance NAME] [--id ID | --url URL] [--like] [--collect] [--comment TEXT]
|
|
20
|
+
bun ./scripts/rednote/interact.ts [--instance NAME] [--id ID | --url URL] [--like] [--collect] [--comment TEXT]
|
|
20
21
|
|
|
21
22
|
Options:
|
|
22
23
|
--instance NAME Optional. Defaults to the saved lastConnect instance
|
|
23
|
-
--
|
|
24
|
+
--id ID Optional. Database record id from home/search output
|
|
25
|
+
--url URL Optional. Xiaohongshu explore url
|
|
24
26
|
--like Optional. Perform like
|
|
25
27
|
--collect Optional. Perform collect
|
|
26
28
|
--comment TEXT Optional. Post comment content
|
|
@@ -36,6 +38,9 @@ export function parseInteractCliArgs(argv) {
|
|
|
36
38
|
instance: {
|
|
37
39
|
type: 'string'
|
|
38
40
|
},
|
|
41
|
+
id: {
|
|
42
|
+
type: 'string'
|
|
43
|
+
},
|
|
39
44
|
url: {
|
|
40
45
|
type: 'string'
|
|
41
46
|
},
|
|
@@ -59,6 +64,7 @@ export function parseInteractCliArgs(argv) {
|
|
|
59
64
|
}
|
|
60
65
|
return {
|
|
61
66
|
instance: values.instance,
|
|
67
|
+
id: values.id,
|
|
62
68
|
url: values.url,
|
|
63
69
|
like: values.like,
|
|
64
70
|
collect: values.collect,
|
|
@@ -253,8 +259,8 @@ export async function interactWithFeed(session, url, actions, commentContent) {
|
|
|
253
259
|
}
|
|
254
260
|
const page = await getOrCreateXiaohongshuPage(session);
|
|
255
261
|
await waitForInteractContainer(page);
|
|
256
|
-
let liked = detailItem.note.
|
|
257
|
-
let collected = detailItem.note.
|
|
262
|
+
let liked = detailItem.note.liked === true;
|
|
263
|
+
let collected = detailItem.note.collected === true;
|
|
258
264
|
const messages = [];
|
|
259
265
|
for (const action of actions){
|
|
260
266
|
if (action === 'like') {
|
|
@@ -280,17 +286,34 @@ export async function interactWithFeed(session, url, actions, commentContent) {
|
|
|
280
286
|
message: `${messages.join('; ')}: ${url}`
|
|
281
287
|
};
|
|
282
288
|
}
|
|
289
|
+
async function resolveInteractUrl(values, instanceName) {
|
|
290
|
+
if (values.id) {
|
|
291
|
+
if (!instanceName) {
|
|
292
|
+
throw new Error('The --id option requires an instance-backed session.');
|
|
293
|
+
}
|
|
294
|
+
const url = await findPersistedPostUrlByRecordId(instanceName, ensureNonEmpty(values.id, '--id'));
|
|
295
|
+
if (!url) {
|
|
296
|
+
throw new Error(`No saved post url found for id: ${values.id}`);
|
|
297
|
+
}
|
|
298
|
+
return url;
|
|
299
|
+
}
|
|
300
|
+
if (values.url) {
|
|
301
|
+
return ensureNonEmpty(values.url, '--url');
|
|
302
|
+
}
|
|
303
|
+
throw new Error('Missing required option: --id or --url');
|
|
304
|
+
}
|
|
283
305
|
export async function runInteractCommand(values = {}) {
|
|
284
306
|
if (values.help) {
|
|
285
307
|
printInteractHelp();
|
|
286
308
|
return;
|
|
287
309
|
}
|
|
288
|
-
const url = ensureNonEmpty(values.url, '--url');
|
|
289
310
|
const { actions, commentContent } = resolveInteractActions(values);
|
|
311
|
+
await initializeRednoteDatabase();
|
|
290
312
|
const target = resolveStatusTarget(values.instance);
|
|
291
313
|
const session = await createRednoteSession(target);
|
|
292
314
|
try {
|
|
293
315
|
await ensureRednoteLoggedIn(target, `performing ${actions.join(', ')} interact`, session);
|
|
316
|
+
const url = await resolveInteractUrl(values, target.instanceName);
|
|
294
317
|
const result = await interactWithFeed(session, url, actions, commentContent);
|
|
295
318
|
printJson(result);
|
|
296
319
|
} finally{
|