opencli-plugin-socialdatax 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/lib/socialdatax-core.mjs +303 -0
- package/opencli-plugin.json +25 -0
- package/package.json +44 -0
- package/skills/socialdatax-opencli/SKILL.md +46 -0
- package/socialdatax.ts +145 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SocialDataX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SocialDataX OpenCLI Plugin | 社媒数据助手 OpenCLI 插件
|
|
2
|
+
|
|
3
|
+
SocialDataX / 社媒数据助手 provides hosted, read-only social data tools for 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin workflows.
|
|
4
|
+
|
|
5
|
+
Use this OpenCLI plugin when you want API Key based data access without relying on a logged-in browser session. It is a complement to OpenCLI's built-in browser-backed `xiaohongshu` adapter, not a replacement for that adapter.
|
|
6
|
+
|
|
7
|
+
- Product: `SocialDataX` / `社媒数据助手`
|
|
8
|
+
- Website: <https://socialdatax.com>
|
|
9
|
+
- Platforms: 小红书 / Xiaohongshu / XHS / RedNote, 抖音 / Douyin
|
|
10
|
+
- Runtime package: `socialdatax-skills@latest`
|
|
11
|
+
- API key environment variable: `SOCIALDATAX_API_KEY`
|
|
12
|
+
|
|
13
|
+
## Local Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
opencli plugin install /Users/devin/work/cx/code/social-service/public-listings/socialdatax-opencli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Set your API Key before data calls:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
export SOCIALDATAX_API_KEY="<SOCIALDATAX_API_KEY>"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
opencli socialdatax xhs-search --keyword "露营桌" -f json
|
|
29
|
+
opencli socialdatax xhs-detail --note-id "<note_id>" -f json
|
|
30
|
+
opencli socialdatax xhs-comments --note-id "<note_id>" -f table
|
|
31
|
+
opencli socialdatax xhs-sub-comments --note-id "<note_id>" --comment-id "<comment_id>" -f table
|
|
32
|
+
opencli socialdatax xhs-user-info --user-id "<user_id>" -f json
|
|
33
|
+
opencli socialdatax xhs-user-posts --user-id "<user_id>" -f table
|
|
34
|
+
|
|
35
|
+
opencli socialdatax douyin-search --keyword "露营桌" -f json
|
|
36
|
+
opencli socialdatax douyin-detail --aweme-id "<aweme_id>" -f json
|
|
37
|
+
opencli socialdatax douyin-comments --aweme-id "<aweme_id>" -f table
|
|
38
|
+
opencli socialdatax douyin-user-info --sec-user-id "<sec_user_id>" -f json
|
|
39
|
+
opencli socialdatax douyin-user-posts --sec-user-id "<sec_user_id>" -f table
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Each command forwards to `npx -y socialdatax-skills@latest ...` and flattens returned JSON so OpenCLI can render table, JSON, YAML, or CSV output.
|
|
43
|
+
|
|
44
|
+
## Read-Only Boundary
|
|
45
|
+
|
|
46
|
+
This plugin only reads public content data through hosted SocialDataX services. It does not log in to user accounts, read browser data, publish posts, like, comment, follow, edit, delete, or perform account actions.
|
|
47
|
+
|
|
48
|
+
## Search Aliases
|
|
49
|
+
|
|
50
|
+
Users and agents may search for this plugin with:
|
|
51
|
+
|
|
52
|
+
- `SocialDataX`
|
|
53
|
+
- `社媒数据助手`
|
|
54
|
+
- `小红书`
|
|
55
|
+
- `Xiaohongshu`
|
|
56
|
+
- `XHS`
|
|
57
|
+
- `RedNote`
|
|
58
|
+
- `抖音`
|
|
59
|
+
- `Douyin`
|
|
60
|
+
- `social media data`
|
|
61
|
+
- `creator analytics`
|
|
62
|
+
- `comment analysis`
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export const PRODUCT_NAME = "SocialDataX / 社媒数据助手";
|
|
4
|
+
export const PRIMARY_API_KEY_ENV = "SOCIALDATAX_API_KEY";
|
|
5
|
+
export const DEFAULT_PACKAGE_SPEC = "socialdatax-skills@latest";
|
|
6
|
+
|
|
7
|
+
const TOOL_COMMANDS = {
|
|
8
|
+
"xhs-search": {
|
|
9
|
+
platform: "xhs",
|
|
10
|
+
action: "search",
|
|
11
|
+
listPath: ["data", "items"],
|
|
12
|
+
pageMeta: ["next_page", "total_count"],
|
|
13
|
+
options: {
|
|
14
|
+
keyword: "--keyword",
|
|
15
|
+
page: "--page",
|
|
16
|
+
sortType: "--sort-type",
|
|
17
|
+
noteType: "--note-type",
|
|
18
|
+
publishTimeRange: "--publish-time-range",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"xhs-detail": {
|
|
22
|
+
platform: "xhs",
|
|
23
|
+
action: "detail",
|
|
24
|
+
options: {
|
|
25
|
+
noteId: "--note-id",
|
|
26
|
+
url: "--url",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
"xhs-comments": {
|
|
30
|
+
platform: "xhs",
|
|
31
|
+
action: "comments",
|
|
32
|
+
listPath: ["data", "items"],
|
|
33
|
+
pageMeta: ["next_page_token", "comment_count", "top_level_comment_count"],
|
|
34
|
+
options: {
|
|
35
|
+
noteId: "--note-id",
|
|
36
|
+
url: "--url",
|
|
37
|
+
pageToken: "--page-token",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
"xhs-sub-comments": {
|
|
41
|
+
platform: "xhs",
|
|
42
|
+
action: "sub-comments",
|
|
43
|
+
listPath: ["data", "items"],
|
|
44
|
+
pageMeta: ["next_page_token"],
|
|
45
|
+
options: {
|
|
46
|
+
noteId: "--note-id",
|
|
47
|
+
commentId: "--comment-id",
|
|
48
|
+
pageToken: "--page-token",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"xhs-user-info": {
|
|
52
|
+
platform: "xhs",
|
|
53
|
+
action: "user-info",
|
|
54
|
+
options: {
|
|
55
|
+
userId: "--user-id",
|
|
56
|
+
profileUrl: "--profile-url",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
"xhs-user-posts": {
|
|
60
|
+
platform: "xhs",
|
|
61
|
+
action: "user-posts",
|
|
62
|
+
listPath: ["data", "items"],
|
|
63
|
+
pageMeta: ["next_page_token"],
|
|
64
|
+
options: {
|
|
65
|
+
userId: "--user-id",
|
|
66
|
+
profileUrl: "--profile-url",
|
|
67
|
+
pageToken: "--page-token",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"douyin-search": {
|
|
71
|
+
platform: "douyin",
|
|
72
|
+
action: "search",
|
|
73
|
+
listPath: ["data", "items"],
|
|
74
|
+
pageMeta: ["next_page_token"],
|
|
75
|
+
options: {
|
|
76
|
+
keyword: "--keyword",
|
|
77
|
+
pageToken: "--page-token",
|
|
78
|
+
sortType: "--sort-type",
|
|
79
|
+
publishTimeRange: "--publish-time-range",
|
|
80
|
+
durationRange: "--duration-range",
|
|
81
|
+
contentType: "--content-type",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
"douyin-detail": {
|
|
85
|
+
platform: "douyin",
|
|
86
|
+
action: "detail",
|
|
87
|
+
options: {
|
|
88
|
+
awemeId: "--aweme-id",
|
|
89
|
+
url: "--url",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
"douyin-comments": {
|
|
93
|
+
platform: "douyin",
|
|
94
|
+
action: "comments",
|
|
95
|
+
listPath: ["data", "items"],
|
|
96
|
+
pageMeta: ["next_page_token", "comment_count"],
|
|
97
|
+
options: {
|
|
98
|
+
awemeId: "--aweme-id",
|
|
99
|
+
url: "--url",
|
|
100
|
+
pageToken: "--page-token",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
"douyin-user-info": {
|
|
104
|
+
platform: "douyin",
|
|
105
|
+
action: "user-info",
|
|
106
|
+
options: {
|
|
107
|
+
secUserId: "--sec-user-id",
|
|
108
|
+
profileUrl: "--profile-url",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
"douyin-user-posts": {
|
|
112
|
+
platform: "douyin",
|
|
113
|
+
action: "user-posts",
|
|
114
|
+
listPath: ["data", "items"],
|
|
115
|
+
pageMeta: ["next_page_token"],
|
|
116
|
+
options: {
|
|
117
|
+
secUserId: "--sec-user-id",
|
|
118
|
+
profileUrl: "--profile-url",
|
|
119
|
+
pageToken: "--page-token",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const OPTION_ALIASES = {
|
|
125
|
+
"aweme-id": "awemeId",
|
|
126
|
+
"comment-id": "commentId",
|
|
127
|
+
"content-type": "contentType",
|
|
128
|
+
"duration-range": "durationRange",
|
|
129
|
+
keyword: "keyword",
|
|
130
|
+
"note-id": "noteId",
|
|
131
|
+
"note-type": "noteType",
|
|
132
|
+
page: "page",
|
|
133
|
+
"page-token": "pageToken",
|
|
134
|
+
"profile-url": "profileUrl",
|
|
135
|
+
"publish-time-range": "publishTimeRange",
|
|
136
|
+
"sec-user-id": "secUserId",
|
|
137
|
+
"sort-type": "sortType",
|
|
138
|
+
url: "url",
|
|
139
|
+
"user-id": "userId",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export function listSocialDataXCommands() {
|
|
143
|
+
return Object.keys(TOOL_COMMANDS);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function normalizeCommandName(commandName) {
|
|
147
|
+
return commandName.replace(/^socialdatax[-:]/, "");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function normalizeOptions(input = {}) {
|
|
151
|
+
const normalized = {};
|
|
152
|
+
for (const [key, value] of Object.entries(input)) {
|
|
153
|
+
const canonical = OPTION_ALIASES[key] || key;
|
|
154
|
+
normalized[canonical] = value;
|
|
155
|
+
}
|
|
156
|
+
return normalized;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function buildSocialDataXArgs(commandName, inputOptions = {}) {
|
|
160
|
+
const normalizedCommand = normalizeCommandName(commandName);
|
|
161
|
+
const command = TOOL_COMMANDS[normalizedCommand];
|
|
162
|
+
if (!command) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Unsupported SocialDataX command "${commandName}". Use one of: ${listSocialDataXCommands().join(", ")}.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const options = normalizeOptions(inputOptions);
|
|
169
|
+
const args = [command.platform, command.action];
|
|
170
|
+
for (const [optionKey, cliFlag] of Object.entries(command.options)) {
|
|
171
|
+
const value = options[optionKey];
|
|
172
|
+
if (value === undefined || value === null || value === false || value === "") {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
args.push(cliFlag, String(value));
|
|
176
|
+
}
|
|
177
|
+
return args;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function buildSocialDataXProcess(commandName, inputOptions = {}, env = process.env) {
|
|
181
|
+
const apiKey = env[PRIMARY_API_KEY_ENV];
|
|
182
|
+
if (!apiKey) {
|
|
183
|
+
throw new Error(`Missing API Key. Set ${PRIMARY_API_KEY_ENV} before running SocialDataX OpenCLI commands.`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const mockBin = env.SOCIALDATAX_OPENCLI_SKILLS_BIN;
|
|
187
|
+
if (mockBin) {
|
|
188
|
+
return {
|
|
189
|
+
file: mockBin,
|
|
190
|
+
args: buildSocialDataXArgs(commandName, inputOptions),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
file: "npx",
|
|
196
|
+
args: ["-y", env.SOCIALDATAX_OPENCLI_PACKAGE_SPEC || DEFAULT_PACKAGE_SPEC, ...buildSocialDataXArgs(commandName, inputOptions)],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function runSocialDataXCommand(commandName, inputOptions = {}, runtime = {}) {
|
|
201
|
+
const env = runtime.env || process.env;
|
|
202
|
+
const spawn = runtime.spawn || spawnJsonCommand;
|
|
203
|
+
const processSpec = buildSocialDataXProcess(commandName, inputOptions, env);
|
|
204
|
+
const envelope = await spawn(processSpec.file, processSpec.args, {
|
|
205
|
+
env: { ...env },
|
|
206
|
+
});
|
|
207
|
+
return flattenSocialDataXEnvelope(commandName, envelope);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function spawnJsonCommand(file, args, options = {}) {
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
const child = spawn(file, args, {
|
|
213
|
+
env: options.env,
|
|
214
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
215
|
+
});
|
|
216
|
+
let stdout = "";
|
|
217
|
+
let stderr = "";
|
|
218
|
+
child.stdout.setEncoding("utf8");
|
|
219
|
+
child.stderr.setEncoding("utf8");
|
|
220
|
+
child.stdout.on("data", (chunk) => {
|
|
221
|
+
stdout += chunk;
|
|
222
|
+
});
|
|
223
|
+
child.stderr.on("data", (chunk) => {
|
|
224
|
+
stderr += chunk;
|
|
225
|
+
});
|
|
226
|
+
child.on("error", reject);
|
|
227
|
+
child.on("close", (status) => {
|
|
228
|
+
if (status !== 0) {
|
|
229
|
+
reject(new Error(trimError(stderr) || `socialdatax-skills exited with status ${status}.`));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
resolve(JSON.parse(stdout));
|
|
234
|
+
} catch (error) {
|
|
235
|
+
reject(new Error(`Failed to parse socialdatax-skills JSON output: ${error.message}`));
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function flattenSocialDataXEnvelope(commandName, envelope) {
|
|
242
|
+
const normalizedCommand = normalizeCommandName(commandName);
|
|
243
|
+
const command = TOOL_COMMANDS[normalizedCommand];
|
|
244
|
+
if (!command) {
|
|
245
|
+
throw new Error(`Unsupported SocialDataX command "${commandName}".`);
|
|
246
|
+
}
|
|
247
|
+
if (!command.listPath) {
|
|
248
|
+
return [flattenObject(envelope?.data ?? envelope)];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const items = getPath(envelope, command.listPath);
|
|
252
|
+
if (!Array.isArray(items)) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const meta = {};
|
|
257
|
+
for (const key of command.pageMeta || []) {
|
|
258
|
+
if (envelope?.data && Object.prototype.hasOwnProperty.call(envelope.data, key)) {
|
|
259
|
+
meta[key] = envelope.data[key];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return items.map((item) => flattenObject({ ...meta, ...item }));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function flattenObject(value, prefix = "", output = {}) {
|
|
266
|
+
if (value === null || value === undefined) {
|
|
267
|
+
if (prefix) {
|
|
268
|
+
output[prefix] = value ?? "";
|
|
269
|
+
}
|
|
270
|
+
return output;
|
|
271
|
+
}
|
|
272
|
+
if (Array.isArray(value)) {
|
|
273
|
+
if (prefix) {
|
|
274
|
+
output[prefix] = JSON.stringify(value);
|
|
275
|
+
}
|
|
276
|
+
return output;
|
|
277
|
+
}
|
|
278
|
+
if (typeof value !== "object") {
|
|
279
|
+
if (prefix) {
|
|
280
|
+
output[prefix] = value;
|
|
281
|
+
}
|
|
282
|
+
return output;
|
|
283
|
+
}
|
|
284
|
+
for (const [key, child] of Object.entries(value)) {
|
|
285
|
+
const childKey = prefix ? `${prefix}_${key}` : key;
|
|
286
|
+
if (child && typeof child === "object" && !Array.isArray(child)) {
|
|
287
|
+
flattenObject(child, childKey, output);
|
|
288
|
+
} else if (Array.isArray(child)) {
|
|
289
|
+
output[childKey] = JSON.stringify(child);
|
|
290
|
+
} else {
|
|
291
|
+
output[childKey] = child ?? "";
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return output;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getPath(value, path) {
|
|
298
|
+
return path.reduce((current, key) => current?.[key], value);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function trimError(text) {
|
|
302
|
+
return String(text || "").trim();
|
|
303
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "socialdatax",
|
|
3
|
+
"displayName": "SocialDataX / 社媒数据助手",
|
|
4
|
+
"description": "SocialDataX / 社媒数据助手 provides hosted read-only social data tools for 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin: search content, read details, analyze comments, creator profiles, and creator posts.",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"opencli": ">=1.0.0",
|
|
7
|
+
"homepage": "https://socialdatax.com",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"SocialDataX",
|
|
10
|
+
"社媒数据助手",
|
|
11
|
+
"小红书",
|
|
12
|
+
"Xiaohongshu",
|
|
13
|
+
"XHS",
|
|
14
|
+
"RedNote",
|
|
15
|
+
"抖音",
|
|
16
|
+
"Douyin",
|
|
17
|
+
"social media data",
|
|
18
|
+
"content insights",
|
|
19
|
+
"creator analytics",
|
|
20
|
+
"comments"
|
|
21
|
+
],
|
|
22
|
+
"commands": [
|
|
23
|
+
"socialdatax"
|
|
24
|
+
]
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencli-plugin-socialdatax",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SocialDataX / 社媒数据助手 OpenCLI plugin for 小红书, Xiaohongshu, XHS, RedNote, 抖音, and Douyin read-only social data workflows.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"README.md",
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"opencli-plugin.json",
|
|
11
|
+
"socialdatax.ts",
|
|
12
|
+
"lib/**",
|
|
13
|
+
"skills/**"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node --test test/*.test.mjs"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"socialdatax",
|
|
20
|
+
"社媒数据助手",
|
|
21
|
+
"opencli",
|
|
22
|
+
"opencli-plugin",
|
|
23
|
+
"xiaohongshu",
|
|
24
|
+
"小红书",
|
|
25
|
+
"xhs",
|
|
26
|
+
"rednote",
|
|
27
|
+
"douyin",
|
|
28
|
+
"抖音",
|
|
29
|
+
"social-media",
|
|
30
|
+
"social-data",
|
|
31
|
+
"content-analysis",
|
|
32
|
+
"creator-analytics"
|
|
33
|
+
],
|
|
34
|
+
"homepage": "https://socialdatax.com",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://socialdatax.com"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=21.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@jackwener/opencli": ">=1.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: socialdatax-opencli
|
|
3
|
+
description: Use SocialDataX / 社媒数据助手 through OpenCLI for read-only 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin social data workflows including search, details, comments, creator profiles, and creator posts.
|
|
4
|
+
metadata:
|
|
5
|
+
opencli:
|
|
6
|
+
requires:
|
|
7
|
+
env:
|
|
8
|
+
- SOCIALDATAX_API_KEY
|
|
9
|
+
bins:
|
|
10
|
+
- opencli
|
|
11
|
+
- node
|
|
12
|
+
- npm
|
|
13
|
+
primaryEnv: SOCIALDATAX_API_KEY
|
|
14
|
+
homepage: https://socialdatax.com
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# SocialDataX OpenCLI
|
|
18
|
+
|
|
19
|
+
Use this skill when the user asks to use OpenCLI for SocialDataX / 社媒数据助手, 小红书 / Xiaohongshu / XHS / RedNote, or 抖音 / Douyin read-only data workflows.
|
|
20
|
+
|
|
21
|
+
Prefer the OpenCLI plugin commands when installed:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
opencli socialdatax xhs-search --keyword "<keyword>" -f json
|
|
25
|
+
opencli socialdatax xhs-detail --note-id "<note_id>" -f json
|
|
26
|
+
opencli socialdatax xhs-comments --note-id "<note_id>" -f table
|
|
27
|
+
opencli socialdatax xhs-sub-comments --note-id "<note_id>" --comment-id "<comment_id>" -f table
|
|
28
|
+
opencli socialdatax xhs-user-info --user-id "<user_id>" -f json
|
|
29
|
+
opencli socialdatax xhs-user-posts --user-id "<user_id>" -f table
|
|
30
|
+
opencli socialdatax douyin-search --keyword "<keyword>" -f json
|
|
31
|
+
opencli socialdatax douyin-detail --aweme-id "<aweme_id>" -f json
|
|
32
|
+
opencli socialdatax douyin-comments --aweme-id "<aweme_id>" -f table
|
|
33
|
+
opencli socialdatax douyin-user-info --sec-user-id "<sec_user_id>" -f json
|
|
34
|
+
opencli socialdatax douyin-user-posts --sec-user-id "<sec_user_id>" -f table
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If the OpenCLI plugin is not installed, fall back to the direct CLI:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx -y socialdatax-skills@latest xhs search --keyword "<keyword>" --pretty
|
|
41
|
+
npx -y socialdatax-skills@latest douyin search --keyword "<keyword>" --pretty
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Set `SOCIALDATAX_API_KEY` before data calls. This skill is read-only: do not use it for login, posting, liking, commenting, following, editing, deleting, or other account actions.
|
|
45
|
+
|
|
46
|
+
OpenCLI's built-in `xiaohongshu` adapter is browser-session based. `opencli socialdatax ...` is API Key based hosted data access and covers both 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin.
|
package/socialdatax.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { cli, Strategy } from "@jackwener/opencli/registry";
|
|
2
|
+
import { runSocialDataXCommand } from "./lib/socialdatax-core.mjs";
|
|
3
|
+
|
|
4
|
+
const COMMON_COLUMNS = [
|
|
5
|
+
"note_id",
|
|
6
|
+
"note_url",
|
|
7
|
+
"aweme_id",
|
|
8
|
+
"sec_user_id",
|
|
9
|
+
"comment_id",
|
|
10
|
+
"title",
|
|
11
|
+
"desc",
|
|
12
|
+
"content",
|
|
13
|
+
"nickname",
|
|
14
|
+
"author_nickname",
|
|
15
|
+
"profile_nickname",
|
|
16
|
+
"like_count",
|
|
17
|
+
"comment_count",
|
|
18
|
+
"top_level_comment_count",
|
|
19
|
+
"next_page",
|
|
20
|
+
"next_page_token",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const pageTokenOption = {
|
|
24
|
+
name: "page-token",
|
|
25
|
+
type: "string",
|
|
26
|
+
help: "Opaque pagination token returned by the previous page.",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function localCommand(name, description, options = {}) {
|
|
30
|
+
return cli({
|
|
31
|
+
site: "socialdatax",
|
|
32
|
+
name,
|
|
33
|
+
description,
|
|
34
|
+
access: "read",
|
|
35
|
+
example: `opencli socialdatax ${name} -f json`,
|
|
36
|
+
strategy: Strategy.LOCAL,
|
|
37
|
+
browser: false,
|
|
38
|
+
args: options,
|
|
39
|
+
columns: COMMON_COLUMNS,
|
|
40
|
+
async func(argv) {
|
|
41
|
+
return runSocialDataXCommand(name, argv);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
localCommand(
|
|
47
|
+
"xhs-search",
|
|
48
|
+
"Search 小红书 / Xiaohongshu / XHS / RedNote notes with SocialDataX / 社媒数据助手.",
|
|
49
|
+
[
|
|
50
|
+
{ name: "keyword", type: "string", required: true, help: "Search keyword." },
|
|
51
|
+
{ name: "page", type: "int", help: "1-based page number." },
|
|
52
|
+
{ name: "sort-type", type: "string", help: "general, time_descending, like_count_descending, comment_count_descending, or collect_count_descending." },
|
|
53
|
+
{ name: "note-type", type: "string", help: "all, image, or video." },
|
|
54
|
+
{ name: "publish-time-range", type: "string", help: "all, day, week, or half_year." },
|
|
55
|
+
]
|
|
56
|
+
);
|
|
57
|
+
localCommand(
|
|
58
|
+
"xhs-detail",
|
|
59
|
+
"Read one 小红书 / Xiaohongshu / XHS / RedNote note detail.",
|
|
60
|
+
[
|
|
61
|
+
{ name: "note-id", type: "string", help: "Known XHS note ID." },
|
|
62
|
+
{ name: "url", type: "string", help: "XHS note URL, short link, share text, or note ID." },
|
|
63
|
+
]
|
|
64
|
+
);
|
|
65
|
+
localCommand(
|
|
66
|
+
"xhs-comments",
|
|
67
|
+
"Fetch 小红书 / Xiaohongshu / XHS / RedNote first-level comments.",
|
|
68
|
+
[
|
|
69
|
+
{ name: "note-id", type: "string", help: "Known XHS note ID." },
|
|
70
|
+
{ name: "url", type: "string", help: "XHS note URL, short link, or share text." },
|
|
71
|
+
pageTokenOption,
|
|
72
|
+
]
|
|
73
|
+
);
|
|
74
|
+
localCommand(
|
|
75
|
+
"xhs-sub-comments",
|
|
76
|
+
"Fetch 小红书 / Xiaohongshu / XHS / RedNote replies under one comment.",
|
|
77
|
+
[
|
|
78
|
+
{ name: "note-id", type: "string", required: true, help: "Known XHS note ID." },
|
|
79
|
+
{ name: "comment-id", type: "string", required: true, help: "First-level comment ID." },
|
|
80
|
+
pageTokenOption,
|
|
81
|
+
]
|
|
82
|
+
);
|
|
83
|
+
localCommand(
|
|
84
|
+
"xhs-user-info",
|
|
85
|
+
"Read 小红书 / Xiaohongshu / XHS / RedNote creator profile data.",
|
|
86
|
+
[
|
|
87
|
+
{ name: "user-id", type: "string", help: "Known XHS user ID." },
|
|
88
|
+
{ name: "profile-url", type: "string", help: "XHS profile URL, short link, or share text." },
|
|
89
|
+
]
|
|
90
|
+
);
|
|
91
|
+
localCommand(
|
|
92
|
+
"xhs-user-posts",
|
|
93
|
+
"Fetch 小红书 / Xiaohongshu / XHS / RedNote creator notes.",
|
|
94
|
+
[
|
|
95
|
+
{ name: "user-id", type: "string", help: "Known XHS user ID." },
|
|
96
|
+
{ name: "profile-url", type: "string", help: "XHS profile URL, short link, or share text." },
|
|
97
|
+
pageTokenOption,
|
|
98
|
+
]
|
|
99
|
+
);
|
|
100
|
+
localCommand(
|
|
101
|
+
"douyin-search",
|
|
102
|
+
"Search 抖音 / Douyin works with SocialDataX / 社媒数据助手.",
|
|
103
|
+
[
|
|
104
|
+
{ name: "keyword", type: "string", required: true, help: "Search keyword." },
|
|
105
|
+
pageTokenOption,
|
|
106
|
+
{ name: "sort-type", type: "string", help: "general, time_descending, or like_count_descending." },
|
|
107
|
+
{ name: "publish-time-range", type: "string", help: "all, day, week, or half_year." },
|
|
108
|
+
{ name: "duration-range", type: "string", help: "all, under_1_minute, one_to_five_minutes, or over_5_minutes." },
|
|
109
|
+
{ name: "content-type", type: "string", help: "all, video, or image." },
|
|
110
|
+
]
|
|
111
|
+
);
|
|
112
|
+
localCommand(
|
|
113
|
+
"douyin-detail",
|
|
114
|
+
"Read one 抖音 / Douyin work detail.",
|
|
115
|
+
[
|
|
116
|
+
{ name: "aweme-id", type: "string", help: "Known Douyin aweme_id." },
|
|
117
|
+
{ name: "url", type: "string", help: "Douyin content URL, short link, or share text." },
|
|
118
|
+
]
|
|
119
|
+
);
|
|
120
|
+
localCommand(
|
|
121
|
+
"douyin-comments",
|
|
122
|
+
"Fetch 抖音 / Douyin first-level comments.",
|
|
123
|
+
[
|
|
124
|
+
{ name: "aweme-id", type: "string", help: "Known Douyin aweme_id." },
|
|
125
|
+
{ name: "url", type: "string", help: "Douyin content URL, short link, or share text." },
|
|
126
|
+
pageTokenOption,
|
|
127
|
+
]
|
|
128
|
+
);
|
|
129
|
+
localCommand(
|
|
130
|
+
"douyin-user-info",
|
|
131
|
+
"Read 抖音 / Douyin creator profile data.",
|
|
132
|
+
[
|
|
133
|
+
{ name: "sec-user-id", type: "string", help: "Known Douyin sec_user_id." },
|
|
134
|
+
{ name: "profile-url", type: "string", help: "Douyin profile URL, short link, or share text." },
|
|
135
|
+
]
|
|
136
|
+
);
|
|
137
|
+
localCommand(
|
|
138
|
+
"douyin-user-posts",
|
|
139
|
+
"Fetch 抖音 / Douyin creator works.",
|
|
140
|
+
[
|
|
141
|
+
{ name: "sec-user-id", type: "string", help: "Known Douyin sec_user_id." },
|
|
142
|
+
{ name: "profile-url", type: "string", help: "Douyin profile URL, short link, or share text." },
|
|
143
|
+
pageTokenOption,
|
|
144
|
+
]
|
|
145
|
+
);
|