socialdatax-skills 0.2.22 → 0.2.24
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
CHANGED
|
@@ -73,7 +73,7 @@ Common search phrases for this skill package:
|
|
|
73
73
|
- Sensitive Words Check MCP server name: `sensitive-check`
|
|
74
74
|
- Sensitive Words Check future MCP registry name: `com.socialdatax/sensitive-check`
|
|
75
75
|
- Unified MCP registry name: none; this package installs skills and calls explicit platform services.
|
|
76
|
-
- Current public capability version: `0.2.
|
|
76
|
+
- Current public capability version: `0.2.24`
|
|
77
77
|
|
|
78
78
|
## Direct CLI
|
|
79
79
|
|
|
@@ -87,6 +87,7 @@ npx -y socialdatax-skills@latest xhs search --keyword "露营" --since-days 7 --
|
|
|
87
87
|
npx -y socialdatax-skills@latest xhs hot-search --pretty
|
|
88
88
|
npx -y socialdatax-skills@latest xhs detail --note-id "<note_id>" --pretty
|
|
89
89
|
npx -y socialdatax-skills@latest xhs comments --note-id "<note_id>" --pretty
|
|
90
|
+
npx -y socialdatax-skills@latest xhs comments --note-id "<note_id>" --sort-type time_descending --pretty
|
|
90
91
|
npx -y socialdatax-skills@latest xhs comments --note-id "<note_id>" --all --include-replies --pretty
|
|
91
92
|
npx -y socialdatax-skills@latest xhs sub-comments --note-id "<note_id>" --comment-id "<comment_id>" --pretty
|
|
92
93
|
npx -y socialdatax-skills@latest xhs user-info --user-id "<user_id>" --pretty
|
|
@@ -186,6 +187,10 @@ XHS, Douyin, Kuaishou, Weibo, and WeChat Channels search use `--keyword` and opt
|
|
|
186
187
|
Kuaishou work search uses `kuaishou search --keyword` and optional `--page-token`; Kuaishou creator search uses `kuaishou user-search --keyword` and optional `--page-token`. Kuaishou search does not accept Douyin semantic filters, and `kuaishou user-search` does not support `--since-days`.
|
|
187
188
|
Weibo and WeChat Channels search use `--keyword` and optional `--page-token`.
|
|
188
189
|
|
|
190
|
+
XHS comments accept optional `--sort-type` values: `default`,
|
|
191
|
+
`time_descending`, and `like_count_descending`; omit it for the platform
|
|
192
|
+
default comment order.
|
|
193
|
+
|
|
189
194
|
Douyin search filters use semantic values: `--sort-type` supports `general`,
|
|
190
195
|
`time_descending`, and `like_count_descending`; `--publish-time-range` supports
|
|
191
196
|
`all`, `day`, `week`, and `half_year`; `--duration-range` supports `all`,
|
|
@@ -405,8 +410,8 @@ Current Sensitive Words Check workflows include:
|
|
|
405
410
|
| `xhs_search_notes` | Search Xiaohongshu / 小红书 notes by keyword with optional sort, note type, and publish-time filters. |
|
|
406
411
|
| `xhs_get_note_detail_by_note_url` | Resolve a shared XHS link, short link, or share text into structured note details. |
|
|
407
412
|
| `xhs_get_note_detail_by_note_id` | Fetch structured note details when the caller already has a note ID. |
|
|
408
|
-
| `xhs_get_note_comments_by_note_id` | Fetch paginated first-level comments when the caller already has a note ID
|
|
409
|
-
| `xhs_get_note_comments_by_note_url` | Fetch paginated first-level comments directly from a shared note URL, short link, or share text
|
|
413
|
+
| `xhs_get_note_comments_by_note_id` | Fetch paginated first-level comments when the caller already has a note ID; accepts optional comment `sort_type`. |
|
|
414
|
+
| `xhs_get_note_comments_by_note_url` | Fetch paginated first-level comments directly from a shared note URL, short link, or share text; accepts optional comment `sort_type`. |
|
|
410
415
|
| `xhs_get_note_sub_comments_by_comment_id` | Fetch paginated replies under a first-level comment by note ID and comment ID. |
|
|
411
416
|
| `xhs_get_user_info_by_user_id` | Fetch creator profile data when the caller already has a user ID. |
|
|
412
417
|
| `xhs_get_user_info_by_profile_url` | Resolve a profile link, short link, or share text into creator profile data. |
|
package/cli.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
19
|
const PACKAGE_NAME = "socialdatax-skills";
|
|
20
|
-
const PACKAGE_VERSION = "0.2.
|
|
20
|
+
const PACKAGE_VERSION = "0.2.24";
|
|
21
21
|
const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
|
|
22
22
|
const LOG_PREFIX = `[${PACKAGE_NAME}]`;
|
|
23
23
|
const MIN_NODE_VERSION = "20.18.1";
|
|
@@ -29,6 +29,37 @@ const MEDIA_DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
29
29
|
const MEDIA_DOWNLOAD_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
30
30
|
const MEDIA_DECRYPT_CHUNK_SIZE = 64 * 1024;
|
|
31
31
|
const MEDIA_DOWNLOAD_MAX_REDIRECTS = 5;
|
|
32
|
+
const MP4_BOX_HEADER_SIZE = 8;
|
|
33
|
+
const MP4_EXTENDED_BOX_HEADER_SIZE = 16;
|
|
34
|
+
const MP4_MAX_BOX_SCAN_DEPTH = 16;
|
|
35
|
+
const MP4_FULL_BOX_HEADER_SIZE = 4;
|
|
36
|
+
const MP4_STSD_ENTRY_COUNT_SIZE = 4;
|
|
37
|
+
const MP4_VISUAL_SAMPLE_ENTRY_HEADER_SIZE = 78;
|
|
38
|
+
const MP4_VVC_NAL_LENGTH_SIZE_BYTE_OFFSET = 4;
|
|
39
|
+
const MP4_CONTAINER_BOX_TYPES = new Set([
|
|
40
|
+
"dinf",
|
|
41
|
+
"edts",
|
|
42
|
+
"mdia",
|
|
43
|
+
"meta",
|
|
44
|
+
"minf",
|
|
45
|
+
"moof",
|
|
46
|
+
"moov",
|
|
47
|
+
"mfra",
|
|
48
|
+
"stbl",
|
|
49
|
+
"traf",
|
|
50
|
+
"trak",
|
|
51
|
+
"udta",
|
|
52
|
+
]);
|
|
53
|
+
const MP4_VISUAL_SAMPLE_ENTRY_TYPES = new Set([
|
|
54
|
+
"av01",
|
|
55
|
+
"avc1",
|
|
56
|
+
"avc3",
|
|
57
|
+
"encv",
|
|
58
|
+
"hev1",
|
|
59
|
+
"hvc1",
|
|
60
|
+
"vvc1",
|
|
61
|
+
"vvi1",
|
|
62
|
+
]);
|
|
32
63
|
// Keep this local helper scoped to WeChat media host trees; require k for the decrypt key.
|
|
33
64
|
const WECHAT_MEDIA_DOWNLOAD_HOST_SUFFIXES = [
|
|
34
65
|
".wxapp.tc.qq.com",
|
|
@@ -146,6 +177,7 @@ const XHS_DIRECT_ACTION_OPTIONS = {
|
|
|
146
177
|
"all",
|
|
147
178
|
"maxItems",
|
|
148
179
|
"includeReplies",
|
|
180
|
+
"sortType",
|
|
149
181
|
"pretty",
|
|
150
182
|
],
|
|
151
183
|
"sub-comments": [
|
|
@@ -197,6 +229,11 @@ const XHS_SEARCH_SORT_TYPES = [
|
|
|
197
229
|
"comment_count_descending",
|
|
198
230
|
"collect_count_descending",
|
|
199
231
|
];
|
|
232
|
+
const XHS_COMMENT_SORT_TYPES = [
|
|
233
|
+
"default",
|
|
234
|
+
"time_descending",
|
|
235
|
+
"like_count_descending",
|
|
236
|
+
];
|
|
200
237
|
const XHS_LEGACY_SEARCH_SORT_TYPE_ALIASES = {
|
|
201
238
|
popularity_descending: "like_count_descending",
|
|
202
239
|
comment_descending: "comment_count_descending",
|
|
@@ -521,12 +558,13 @@ const PLATFORMS = {
|
|
|
521
558
|
},
|
|
522
559
|
{
|
|
523
560
|
name: "xhs_get_note_comments_by_note_id",
|
|
524
|
-
description:
|
|
561
|
+
description:
|
|
562
|
+
"Fetch paginated first-level comments by note ID with optional sort.",
|
|
525
563
|
},
|
|
526
564
|
{
|
|
527
565
|
name: "xhs_get_note_comments_by_note_url",
|
|
528
566
|
description:
|
|
529
|
-
"Fetch paginated first-level comments from a note URL, short link, or share text.",
|
|
567
|
+
"Fetch paginated first-level comments from a note URL, short link, or share text with optional sort.",
|
|
530
568
|
},
|
|
531
569
|
{
|
|
532
570
|
name: "xhs_get_note_sub_comments_by_comment_id",
|
|
@@ -2061,6 +2099,8 @@ function printHelp() {
|
|
|
2061
2099
|
console.log(" For comments commands, also fetch nested second-level replies for each returned first-level comment.");
|
|
2062
2100
|
console.log(" --sort-type <general|time_descending|like_count_descending|comment_count_descending|collect_count_descending>");
|
|
2063
2101
|
console.log(" XHS sort meanings: general=default, time_descending=newest, like_count_descending=most liked, comment_count_descending=most commented, collect_count_descending=most collected.");
|
|
2102
|
+
console.log(" --sort-type <default|time_descending|like_count_descending>");
|
|
2103
|
+
console.log(" XHS comments sort meanings: default=platform default, time_descending=newest, like_count_descending=most liked.");
|
|
2064
2104
|
console.log(" --note-type <all|image|video> XHS search note type filter; default is all.");
|
|
2065
2105
|
console.log(" --publish-time-range <all|day|week|half_year>");
|
|
2066
2106
|
console.log(" XHS search publish-time filter; default is all.");
|
|
@@ -2355,6 +2395,7 @@ function buildXhsOperation(action, options) {
|
|
|
2355
2395
|
idDisplay: "--note-id",
|
|
2356
2396
|
urlDisplay: "--url",
|
|
2357
2397
|
pageToken: options.pageToken,
|
|
2398
|
+
extraArguments: buildXhsCommentsExtraArguments(options),
|
|
2358
2399
|
})
|
|
2359
2400
|
);
|
|
2360
2401
|
case "sub-comments":
|
|
@@ -2936,6 +2977,7 @@ export async function decryptWechatMediaCommand(
|
|
|
2936
2977
|
encryptedLength,
|
|
2937
2978
|
decodeKey,
|
|
2938
2979
|
});
|
|
2980
|
+
await patchWechatVvcMp4NalLengthMetadata(tempPath);
|
|
2939
2981
|
decrypted = true;
|
|
2940
2982
|
}
|
|
2941
2983
|
await rename(tempPath, outputPath);
|
|
@@ -3185,6 +3227,158 @@ async function decryptWechatMediaFilePrefix(
|
|
|
3185
3227
|
}
|
|
3186
3228
|
}
|
|
3187
3229
|
|
|
3230
|
+
async function patchWechatVvcMp4NalLengthMetadata(filePath) {
|
|
3231
|
+
const file = await openFile(filePath, "r+");
|
|
3232
|
+
try {
|
|
3233
|
+
const stats = await file.stat();
|
|
3234
|
+
await patchMp4ChildBoxes(file, 0, stats.size, 0);
|
|
3235
|
+
} finally {
|
|
3236
|
+
await file.close();
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
async function patchMp4ChildBoxes(file, startOffset, endOffset, depth) {
|
|
3241
|
+
if (
|
|
3242
|
+
depth > MP4_MAX_BOX_SCAN_DEPTH ||
|
|
3243
|
+
startOffset < 0 ||
|
|
3244
|
+
endOffset - startOffset < MP4_BOX_HEADER_SIZE
|
|
3245
|
+
) {
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
let offset = startOffset;
|
|
3250
|
+
while (offset + MP4_BOX_HEADER_SIZE <= endOffset) {
|
|
3251
|
+
const box = await readMp4BoxHeader(file, offset, endOffset);
|
|
3252
|
+
if (!box) {
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
if (box.type === "vvcC") {
|
|
3256
|
+
await patchVvcConfigurationNalLengthSize(file, box);
|
|
3257
|
+
} else {
|
|
3258
|
+
const childRange = mp4ChildBoxRange(box);
|
|
3259
|
+
if (childRange) {
|
|
3260
|
+
await patchMp4ChildBoxes(
|
|
3261
|
+
file,
|
|
3262
|
+
childRange.startOffset,
|
|
3263
|
+
childRange.endOffset,
|
|
3264
|
+
depth + 1
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
offset = box.endOffset;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
async function readMp4BoxHeader(file, offset, parentEndOffset) {
|
|
3273
|
+
const baseHeader = Buffer.allocUnsafe(MP4_BOX_HEADER_SIZE);
|
|
3274
|
+
const baseRead = await file.read(
|
|
3275
|
+
baseHeader,
|
|
3276
|
+
0,
|
|
3277
|
+
MP4_BOX_HEADER_SIZE,
|
|
3278
|
+
offset
|
|
3279
|
+
);
|
|
3280
|
+
if (baseRead.bytesRead !== MP4_BOX_HEADER_SIZE) {
|
|
3281
|
+
return undefined;
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
const size32 = baseHeader.readUInt32BE(0);
|
|
3285
|
+
const type = baseHeader.toString("ascii", 4, 8);
|
|
3286
|
+
let headerSize = MP4_BOX_HEADER_SIZE;
|
|
3287
|
+
let size;
|
|
3288
|
+
if (size32 === 0) {
|
|
3289
|
+
size = parentEndOffset - offset;
|
|
3290
|
+
} else if (size32 === 1) {
|
|
3291
|
+
if (offset + MP4_EXTENDED_BOX_HEADER_SIZE > parentEndOffset) {
|
|
3292
|
+
return undefined;
|
|
3293
|
+
}
|
|
3294
|
+
const extendedSizeBuffer = Buffer.allocUnsafe(8);
|
|
3295
|
+
const extendedRead = await file.read(
|
|
3296
|
+
extendedSizeBuffer,
|
|
3297
|
+
0,
|
|
3298
|
+
8,
|
|
3299
|
+
offset + MP4_BOX_HEADER_SIZE
|
|
3300
|
+
);
|
|
3301
|
+
if (extendedRead.bytesRead !== 8) {
|
|
3302
|
+
return undefined;
|
|
3303
|
+
}
|
|
3304
|
+
const extendedSize = extendedSizeBuffer.readBigUInt64BE(0);
|
|
3305
|
+
if (extendedSize > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
3306
|
+
return undefined;
|
|
3307
|
+
}
|
|
3308
|
+
size = Number(extendedSize);
|
|
3309
|
+
headerSize = MP4_EXTENDED_BOX_HEADER_SIZE;
|
|
3310
|
+
} else {
|
|
3311
|
+
size = size32;
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
if (
|
|
3315
|
+
size < headerSize ||
|
|
3316
|
+
offset + size > parentEndOffset ||
|
|
3317
|
+
!isMp4BoxType(type)
|
|
3318
|
+
) {
|
|
3319
|
+
return undefined;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
return {
|
|
3323
|
+
type,
|
|
3324
|
+
startOffset: offset,
|
|
3325
|
+
headerSize,
|
|
3326
|
+
payloadOffset: offset + headerSize,
|
|
3327
|
+
endOffset: offset + size,
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
function isMp4BoxType(type) {
|
|
3332
|
+
return /^[\x20-\x7e]{4}$/.test(type);
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
function mp4ChildBoxRange(box) {
|
|
3336
|
+
if (box.type === "stsd") {
|
|
3337
|
+
const childStart =
|
|
3338
|
+
box.payloadOffset + MP4_FULL_BOX_HEADER_SIZE + MP4_STSD_ENTRY_COUNT_SIZE;
|
|
3339
|
+
return childStart < box.endOffset
|
|
3340
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3341
|
+
: undefined;
|
|
3342
|
+
}
|
|
3343
|
+
if (box.type === "meta") {
|
|
3344
|
+
const childStart = box.payloadOffset + MP4_FULL_BOX_HEADER_SIZE;
|
|
3345
|
+
return childStart < box.endOffset
|
|
3346
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3347
|
+
: undefined;
|
|
3348
|
+
}
|
|
3349
|
+
if (MP4_VISUAL_SAMPLE_ENTRY_TYPES.has(box.type)) {
|
|
3350
|
+
const childStart = box.payloadOffset + MP4_VISUAL_SAMPLE_ENTRY_HEADER_SIZE;
|
|
3351
|
+
return childStart < box.endOffset
|
|
3352
|
+
? { startOffset: childStart, endOffset: box.endOffset }
|
|
3353
|
+
: undefined;
|
|
3354
|
+
}
|
|
3355
|
+
if (MP4_CONTAINER_BOX_TYPES.has(box.type)) {
|
|
3356
|
+
return box.payloadOffset < box.endOffset
|
|
3357
|
+
? { startOffset: box.payloadOffset, endOffset: box.endOffset }
|
|
3358
|
+
: undefined;
|
|
3359
|
+
}
|
|
3360
|
+
return undefined;
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
async function patchVvcConfigurationNalLengthSize(file, box) {
|
|
3364
|
+
const targetOffset =
|
|
3365
|
+
box.payloadOffset + MP4_VVC_NAL_LENGTH_SIZE_BYTE_OFFSET;
|
|
3366
|
+
if (targetOffset >= box.endOffset) {
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3369
|
+
const buffer = Buffer.allocUnsafe(1);
|
|
3370
|
+
const { bytesRead } = await file.read(buffer, 0, 1, targetOffset);
|
|
3371
|
+
if (bytesRead !== 1) {
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
const patched = (buffer[0] & 0xf8) | 0x07;
|
|
3375
|
+
if (patched === buffer[0]) {
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
buffer[0] = patched;
|
|
3379
|
+
await writeAllToFile(file, buffer, targetOffset);
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3188
3382
|
function isAbortLikeError(error) {
|
|
3189
3383
|
return error?.name === "AbortError" || error?.name === "TimeoutError";
|
|
3190
3384
|
}
|
|
@@ -3500,6 +3694,7 @@ function buildOneOfCall(
|
|
|
3500
3694
|
idDisplay,
|
|
3501
3695
|
urlDisplay,
|
|
3502
3696
|
pageToken,
|
|
3697
|
+
extraArguments = {},
|
|
3503
3698
|
}
|
|
3504
3699
|
) {
|
|
3505
3700
|
const idValue = options[idOption];
|
|
@@ -3516,9 +3711,23 @@ function buildOneOfCall(
|
|
|
3516
3711
|
if (pageToken) {
|
|
3517
3712
|
toolArguments.page_token = pageToken;
|
|
3518
3713
|
}
|
|
3714
|
+
Object.assign(toolArguments, extraArguments);
|
|
3519
3715
|
return { tool, toolArguments };
|
|
3520
3716
|
}
|
|
3521
3717
|
|
|
3718
|
+
function buildXhsCommentsExtraArguments(options) {
|
|
3719
|
+
const toolArguments = {};
|
|
3720
|
+
if (options.sortType !== undefined) {
|
|
3721
|
+
toolArguments.sort_type = parseAllowedStringOption(
|
|
3722
|
+
options.sortType,
|
|
3723
|
+
"--sort-type",
|
|
3724
|
+
XHS_COMMENT_SORT_TYPES,
|
|
3725
|
+
XHS_COMMENT_SORT_TYPES.join(", ")
|
|
3726
|
+
);
|
|
3727
|
+
}
|
|
3728
|
+
return toolArguments;
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3522
3731
|
function buildTranscriptCall(
|
|
3523
3732
|
options,
|
|
3524
3733
|
{
|
package/package.json
CHANGED
|
@@ -142,6 +142,7 @@ npx -y socialdatax-skills@latest wechat replies \
|
|
|
142
142
|
Optional arguments:
|
|
143
143
|
|
|
144
144
|
- XHS `--note-id <note_id>`: use the complete 24-character lowercase hexadecimal `note_id` returned from search, detail, comments, or creator note lists; do not pass only a prefix.
|
|
145
|
+
- XHS comments `--sort-type <default|time_descending|like_count_descending>`: optional first-level comment sort order; omit it for the platform default order.
|
|
145
146
|
- Douyin `--aweme-id <aweme_id>`: preferred when the video ID is already known and should anchor the comment thread.
|
|
146
147
|
- `--url <url_or_share_text>`: use for a content page URL, short link, or share text for first-level comments.
|
|
147
148
|
- Douyin URL safety: do not pass `video.play_url`; use a Douyin content page URL, short link, or share text instead.
|
|
@@ -188,8 +189,8 @@ MCP tools matching the direct CLI commands above:
|
|
|
188
189
|
- `wechat_get_video_comment_replies_by_comment_id`
|
|
189
190
|
|
|
190
191
|
If MCP tools are already available in the current agent, use one of these tools:
|
|
191
|
-
- `xhs_get_note_comments_by_note_id`: use when the complete 24-character lowercase hexadecimal `note_id` is known; do not pass only a prefix
|
|
192
|
-
- `xhs_get_note_comments_by_note_url`: use for note URLs, short links, or share text
|
|
192
|
+
- `xhs_get_note_comments_by_note_id`: use when the complete 24-character lowercase hexadecimal `note_id` is known; do not pass only a prefix; optional `sort_type` accepts `default`, `time_descending`, or `like_count_descending`.
|
|
193
|
+
- `xhs_get_note_comments_by_note_url`: use for note URLs, short links, or share text; optional `sort_type` accepts `default`, `time_descending`, or `like_count_descending`.
|
|
193
194
|
- `xhs_get_note_sub_comments_by_comment_id`: use when the complete 24-character lowercase hexadecimal `note_id` and first-level comment ID are known; do not pass only a note ID prefix.
|
|
194
195
|
- `douyin_get_video_comments_by_aweme_id`: use when the aweme_id is known.
|
|
195
196
|
- `douyin_get_video_comments_by_url`: use for Douyin content page URLs, short links, or share text; do not pass playback URLs such as `video.play_url`.
|
|
@@ -436,7 +436,8 @@ Optional arguments:
|
|
|
436
436
|
- Kuaishou `user-search` supports `--pages <n>` and `--max-items <n>` for bounded creator candidate discovery; it does not support `--since-days`.
|
|
437
437
|
- `--since-days <1-365>`: keep search or creator content-list items whose public `publish_time` is within the last N days. Search stays bounded by `--pages`; creator content lists continue until the publish-time boundary when `--pages` is omitted.
|
|
438
438
|
- `--include-replies`: for first-level comments, also fetch nested second-level replies under each returned comment.
|
|
439
|
-
- `--sort-type
|
|
439
|
+
- XHS comments `--sort-type <default|time_descending|like_count_descending>`: optional first-level comment sort order; omit it for the platform default order.
|
|
440
|
+
- Keyword research filters: use the documented XHS, Douyin, and WeChat Channels search `--sort-type` values; `--note-type`, `--publish-time-range`, `--duration-range`, and `--content-type` apply only where supported. Kuaishou and Weibo search currently use `--keyword` and optional `--page-token`.
|
|
440
441
|
- `--comment-id`: required for reply/sub-comment commands together with the content ID.
|
|
441
442
|
- `--pretty`: output formatting only.
|
|
442
443
|
- `--source-client socialdatax-skills --source-platform npm --source-skill socialdatax-content-research-assistant`: usage attribution for this Agent Skill; keep these values unchanged when running examples from this Skill.
|