aiconductor-cli 1.3.5 → 1.5.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/.agents/plans/2026-10-28T11-20-16-943Z-plan.md +161 -0
- package/AGENTS.md +157 -78
- package/README.md +49 -5
- package/api_docs/douyin_user_videos.md +2 -2
- package/api_docs/wx_history_articles.md +39 -20
- package/bin/aiconductor-cli.js +129 -94
- package/package.json +1 -1
- package/src/api.js +364 -268
- package/src/api.test.js +265 -3
- package/src/commands/content.js +99 -5
- package/src/commands/wx.js +116 -0
- package/src/commands/xhs.js +268 -225
package/src/api.js
CHANGED
|
@@ -1,82 +1,87 @@
|
|
|
1
1
|
const http = require("http");
|
|
2
2
|
const https = require("https");
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const API_HOST = "plugin.aiconductor.fun";
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const API_HOST = "plugin.aiconductor.fun";
|
|
7
7
|
const API_PATH = "/api/html_publish";
|
|
8
8
|
const MD_TO_HTML_API_PATH = "/api/md_to_html";
|
|
9
9
|
const PUBLISHED_WEBPAGES_API_PATH = "/api/published_webpages";
|
|
10
10
|
const DELETE_PUBLISHED_WEBPAGE_API_PATH = "/api/delete_published_webpage";
|
|
11
11
|
const SEARCH_API_PATH = "/api/doubao_search";
|
|
12
12
|
const HOT_LIST_API_PATH = "/api/hot_list_mixed";
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
13
|
+
const XHS_HOT_LIST_API_PATH = "/api/xhs_hot_list";
|
|
14
|
+
const DOUYIN_VIDEO_INFO_PATH = "/api/douyin_video_info";
|
|
15
|
+
const DOUYIN_VIDEO_COMMENTS_PATH = "/api/douyin_video_comments";
|
|
16
|
+
const DOUYIN_USER_VIDEOS_PATH = "/api/douyin_user_videos";
|
|
17
|
+
const DOUYIN_USER_DETAIL_PATH = "/api/douyin_user_detail";
|
|
18
|
+
const DOUYIN_USER_SEARCH_PATH = "/api/douyin_user_search";
|
|
19
|
+
const DOUYIN_VIDEO_SEARCH_PATH = "/api/douyin_video_search";
|
|
19
20
|
const DOUYIN_LINK_TO_TEXT_PATH = "/api/douyin_link_to_text_v2";
|
|
20
21
|
const XHS_NOTE_PUBLISH_PATH = "/api/xhs_note_publish";
|
|
22
|
+
const WX_ARTICLE_DATA_PATH = "/api/wx_article_data";
|
|
23
|
+
const WX_HISTORY_ARTICLES_PATH = "/api/wx_history_articles";
|
|
21
24
|
const R2_UPLOADER_PATH = "/api/r2_uploader";
|
|
22
25
|
const MAX_R2_FILE_SIZE = 100 * 1024 * 1024;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
|
|
26
|
+
// wx_history_articles 的 max_seconds 平台上限(预留 10s 给平台 180s 兜底)
|
|
27
|
+
const WX_HISTORY_MAX_SECONDS = 170;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 解析 API Key,三级优先级:命令行参数 > 系统环境变量 > .env 文件
|
|
31
|
+
* @returns {string|null}
|
|
32
|
+
*/
|
|
33
|
+
function resolveApiKey(optionKey) {
|
|
34
|
+
// 1. 命令行参数
|
|
35
|
+
if (optionKey) return optionKey;
|
|
36
|
+
|
|
37
|
+
// 2. 系统环境变量
|
|
38
|
+
if (process.env.AICONDUCTOR_API_KEY) return process.env.AICONDUCTOR_API_KEY;
|
|
39
|
+
|
|
40
|
+
// 3. 当前目录 .env 文件
|
|
41
|
+
try {
|
|
42
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
43
|
+
if (fs.existsSync(envPath)) {
|
|
44
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
45
|
+
const match = content.match(/^AICONDUCTOR_API_KEY\s*=\s*(.+)$/m);
|
|
46
|
+
if (match) return match[1].trim();
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// .env 不可读,忽略
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 过滤响应中的计费信息
|
|
57
|
+
* @param {object} resp
|
|
58
|
+
* @returns {object}
|
|
59
|
+
*/
|
|
60
|
+
function stripBilling(resp) {
|
|
61
|
+
if (!resp || typeof resp !== "object") return resp;
|
|
62
|
+
const result = { ...resp };
|
|
63
|
+
delete result.billing;
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 校验整数参数,非有效正整数时报错
|
|
69
|
+
* @param {*} value
|
|
70
|
+
* @param {string} name
|
|
71
|
+
*/
|
|
67
72
|
function validateInt(value, name) {
|
|
68
|
-
if (value === undefined || value === null) return;
|
|
69
|
-
const n = Number(value);
|
|
70
|
-
if (!Number.isInteger(n) || n < 0) {
|
|
71
|
-
throw Object.assign(new Error(`${name} 须为非负整数,收到: ${value}`), { type: "validation_error" });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* HTTP POST 请求封装
|
|
77
|
-
* @param {string} host
|
|
78
|
-
* @param {string} path
|
|
79
|
-
* @param {object} body
|
|
73
|
+
if (value === undefined || value === null) return;
|
|
74
|
+
const n = Number(value);
|
|
75
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
76
|
+
throw Object.assign(new Error(`${name} 须为非负整数,收到: ${value}`), { type: "validation_error" });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* HTTP POST 请求封装
|
|
82
|
+
* @param {string} host
|
|
83
|
+
* @param {string} path
|
|
84
|
+
* @param {object} body
|
|
80
85
|
* @param {typeof http|typeof https} [transport]
|
|
81
86
|
* @returns {Promise<object>}
|
|
82
87
|
*/
|
|
@@ -94,36 +99,36 @@ function httpPost(host, requestPath, body, headers = {}, transport = https) {
|
|
|
94
99
|
...headers,
|
|
95
100
|
},
|
|
96
101
|
};
|
|
97
|
-
|
|
102
|
+
|
|
98
103
|
const req = transport.request(options, (res) => {
|
|
99
|
-
let chunks = [];
|
|
100
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
101
|
-
res.on("end", () => {
|
|
102
|
-
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
103
|
-
let parsed;
|
|
104
|
-
try {
|
|
105
|
-
parsed = JSON.parse(raw);
|
|
106
|
-
} catch
|
|
107
|
-
parsed = { success: false, message: raw };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (res.statusCode >= 400) {
|
|
111
|
-
reject({
|
|
112
|
-
type: "http_error",
|
|
113
|
-
statusCode: res.statusCode,
|
|
114
|
-
body: parsed,
|
|
115
|
-
});
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
resolve(parsed);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
req.on("error", (err) => {
|
|
124
|
-
reject({ type: "network_error", message: err.message });
|
|
125
|
-
});
|
|
126
|
-
|
|
104
|
+
let chunks = [];
|
|
105
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
106
|
+
res.on("end", () => {
|
|
107
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
108
|
+
let parsed;
|
|
109
|
+
try {
|
|
110
|
+
parsed = JSON.parse(raw);
|
|
111
|
+
} catch {
|
|
112
|
+
parsed = { success: false, message: raw };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (res.statusCode >= 400) {
|
|
116
|
+
reject({
|
|
117
|
+
type: "http_error",
|
|
118
|
+
statusCode: res.statusCode,
|
|
119
|
+
body: parsed,
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
resolve(parsed);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
req.on("error", (err) => {
|
|
129
|
+
reject({ type: "network_error", message: err.message });
|
|
130
|
+
});
|
|
131
|
+
|
|
127
132
|
req.write(data);
|
|
128
133
|
req.end();
|
|
129
134
|
});
|
|
@@ -145,6 +150,32 @@ function validatePositiveInt(value, name, max) {
|
|
|
145
150
|
return n;
|
|
146
151
|
}
|
|
147
152
|
|
|
153
|
+
/**
|
|
154
|
+
* 校验非空文本参数,去掉首尾空白
|
|
155
|
+
* @param {*} value
|
|
156
|
+
* @param {string} name
|
|
157
|
+
* @returns {string}
|
|
158
|
+
*/
|
|
159
|
+
function requireText(value, name) {
|
|
160
|
+
const text = value === undefined || value === null ? "" : String(value).trim();
|
|
161
|
+
if (!text) {
|
|
162
|
+
throw Object.assign(new Error(`缺少 ${name} 参数`), { type: "validation_error" });
|
|
163
|
+
}
|
|
164
|
+
return text;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 把 HTTP 200 但 success=false 的业务失败转成 api_error(错误分类见 AGENTS.md)
|
|
169
|
+
* @param {object} resp
|
|
170
|
+
* @returns {object}
|
|
171
|
+
*/
|
|
172
|
+
function throwOnBusinessFailure(resp) {
|
|
173
|
+
if (resp && resp.success === false) {
|
|
174
|
+
throw Object.assign(new Error(resp.message || "接口返回失败"), { type: "api_error" });
|
|
175
|
+
}
|
|
176
|
+
return resp;
|
|
177
|
+
}
|
|
178
|
+
|
|
148
179
|
function contentTypeForFilename(filename) {
|
|
149
180
|
const types = {
|
|
150
181
|
".avif": "image/avif",
|
|
@@ -256,26 +287,26 @@ async function r2Upload(apiKey, params) {
|
|
|
256
287
|
}
|
|
257
288
|
return stripBilling(resp);
|
|
258
289
|
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 发布 HTML 到线上
|
|
262
|
-
* @param {string} apiKey
|
|
263
|
-
* @param {string} htmlCode
|
|
264
|
-
* @param {string} [title]
|
|
265
|
-
* @returns {Promise<{success: boolean, message: string, online_url: string}>}
|
|
266
|
-
*/
|
|
267
|
-
async function publishHtml(apiKey, htmlCode, title) {
|
|
268
|
-
const reqBody = {
|
|
269
|
-
api_key: apiKey,
|
|
270
|
-
html_code: htmlCode,
|
|
271
|
-
};
|
|
272
|
-
if (title) reqBody.title = title;
|
|
273
|
-
|
|
274
|
-
const resp = await httpPost(API_HOST, API_PATH, reqBody);
|
|
275
|
-
return stripBilling(resp);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 发布 HTML 到线上
|
|
293
|
+
* @param {string} apiKey
|
|
294
|
+
* @param {string} htmlCode
|
|
295
|
+
* @param {string} [title]
|
|
296
|
+
* @returns {Promise<{success: boolean, message: string, online_url: string}>}
|
|
297
|
+
*/
|
|
298
|
+
async function publishHtml(apiKey, htmlCode, title) {
|
|
299
|
+
const reqBody = {
|
|
300
|
+
api_key: apiKey,
|
|
301
|
+
html_code: htmlCode,
|
|
302
|
+
};
|
|
303
|
+
if (title) reqBody.title = title;
|
|
304
|
+
|
|
305
|
+
const resp = await httpPost(API_HOST, API_PATH, reqBody);
|
|
306
|
+
return stripBilling(resp);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
279
310
|
* 豆包网页/图片搜索
|
|
280
311
|
* @param {string} apiKey
|
|
281
312
|
* @param {{ query: string, searchType: "web"|"image", count?: number, queryRewrite?: boolean, needContent?: boolean, needUrl?: boolean, sites?: string, blockHosts?: string, authInfoLevel?: number, timeRange?: string, contentFormats?: string, industry?: string, imageWidthMin?: number, imageWidthMax?: number, imageHeightMin?: number, imageHeightMax?: number, imageShapes?: string[]|string }} params
|
|
@@ -332,7 +363,7 @@ async function webSearch(apiKey, params) {
|
|
|
332
363
|
if (imageHeightMax !== undefined) reqBody.image_height_max = imageHeightMax;
|
|
333
364
|
if (params.imageShapes !== undefined) reqBody.image_shapes = params.imageShapes;
|
|
334
365
|
}
|
|
335
|
-
|
|
366
|
+
|
|
336
367
|
const resp = await httpPost(API_HOST, SEARCH_API_PATH, reqBody, {
|
|
337
368
|
Authorization: `Bearer ${apiKey}`,
|
|
338
369
|
});
|
|
@@ -380,25 +411,41 @@ async function hotList(apiKey, params) {
|
|
|
380
411
|
}
|
|
381
412
|
return result;
|
|
382
413
|
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
*
|
|
386
|
-
* @param {string} apiKey
|
|
387
|
-
* @
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* 获取小红书实时热榜。
|
|
417
|
+
* @param {string} apiKey
|
|
418
|
+
* @returns {Promise<object>}
|
|
419
|
+
*/
|
|
420
|
+
async function xhsHotList(apiKey) {
|
|
421
|
+
const resp = await httpPost(API_HOST, XHS_HOT_LIST_API_PATH, {}, {
|
|
422
|
+
Authorization: `Bearer ${apiKey}`,
|
|
423
|
+
}, http);
|
|
424
|
+
const result = stripBilling(resp);
|
|
425
|
+
if (result?.success === false) {
|
|
426
|
+
throw Object.assign(new Error(result.message || "小红书热榜接口返回失败"), { type: "api_error" });
|
|
427
|
+
}
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Markdown 转 HTML 并发布
|
|
433
|
+
* @param {string} apiKey
|
|
434
|
+
* @param {string} mdText
|
|
435
|
+
* @param {string} [filename]
|
|
436
|
+
* @param {boolean} [addCopyButton=true]
|
|
437
|
+
* @returns {Promise<{success: boolean, message: string, file_url: string, online_url: string}>}
|
|
438
|
+
*/
|
|
392
439
|
async function publishMdToHtml(apiKey, mdText, filename, addCopyButton = false) {
|
|
393
|
-
const reqBody = {
|
|
394
|
-
api_key: apiKey,
|
|
395
|
-
md_text: mdText,
|
|
396
|
-
add_copy_button: addCopyButton,
|
|
397
|
-
};
|
|
398
|
-
if (filename) reqBody.filename = filename;
|
|
399
|
-
|
|
400
|
-
const resp = await httpPost(API_HOST, MD_TO_HTML_API_PATH, reqBody);
|
|
401
|
-
|
|
440
|
+
const reqBody = {
|
|
441
|
+
api_key: apiKey,
|
|
442
|
+
md_text: mdText,
|
|
443
|
+
add_copy_button: addCopyButton,
|
|
444
|
+
};
|
|
445
|
+
if (filename) reqBody.filename = filename;
|
|
446
|
+
|
|
447
|
+
const resp = await httpPost(API_HOST, MD_TO_HTML_API_PATH, reqBody);
|
|
448
|
+
|
|
402
449
|
return stripBilling(resp);
|
|
403
450
|
}
|
|
404
451
|
|
|
@@ -437,152 +484,201 @@ async function deletePublishedWebpage(apiKey, webpageId) {
|
|
|
437
484
|
|
|
438
485
|
return stripBilling(resp);
|
|
439
486
|
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* 获取抖音作品详情
|
|
443
|
-
* @param {string} apiKey
|
|
444
|
-
* @param {string} shareUrl
|
|
445
|
-
* @returns {Promise<object>}
|
|
446
|
-
*/
|
|
447
|
-
async function douyinVideoInfo(apiKey, shareUrl) {
|
|
448
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_INFO_PATH, {
|
|
449
|
-
api_key: apiKey,
|
|
450
|
-
share_url: shareUrl,
|
|
451
|
-
});
|
|
452
|
-
return stripBilling(resp);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* 获取抖音作品评论
|
|
457
|
-
* @param {string} apiKey
|
|
458
|
-
* @param {string} videoUrl
|
|
459
|
-
* @param {number} [maxCount=50]
|
|
460
|
-
* @returns {Promise<object>}
|
|
461
|
-
*/
|
|
462
|
-
async function douyinVideoComments(apiKey, videoUrl, maxCount) {
|
|
463
|
-
const body = { api_key: apiKey, video_url: videoUrl };
|
|
464
|
-
if (maxCount !== undefined && maxCount !== null) body.max_count = maxCount;
|
|
465
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_COMMENTS_PATH, body);
|
|
466
|
-
return stripBilling(resp);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* 获取抖音用户视频列表
|
|
471
|
-
* @param {string} apiKey
|
|
472
|
-
* @param {string} homePage
|
|
473
|
-
* @param {number} [maxSeconds=50]
|
|
474
|
-
* @param {number} [maxPages=1]
|
|
475
|
-
* @returns {Promise<object>}
|
|
476
|
-
*/
|
|
477
|
-
async function douyinUserVideos(apiKey, homePage, maxSeconds, maxPages) {
|
|
478
|
-
const body = { api_key: apiKey, home_page: homePage };
|
|
479
|
-
if (maxSeconds !== undefined && maxSeconds !== null) body.max_seconds = maxSeconds;
|
|
480
|
-
if (maxPages !== undefined && maxPages !== null) body.max_pages = maxPages;
|
|
481
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_VIDEOS_PATH, body);
|
|
482
|
-
return stripBilling(resp);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* 获取抖音用户详情
|
|
487
|
-
* @param {string} apiKey
|
|
488
|
-
* @param {string} homePage
|
|
489
|
-
* @returns {Promise<object>}
|
|
490
|
-
*/
|
|
491
|
-
async function douyinUserDetail(apiKey, homePage) {
|
|
492
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_DETAIL_PATH, {
|
|
493
|
-
api_key: apiKey,
|
|
494
|
-
home_page: homePage,
|
|
495
|
-
});
|
|
496
|
-
return stripBilling(resp);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* 搜索抖音用户
|
|
501
|
-
* @param {string} apiKey
|
|
502
|
-
* @param {string} keyword
|
|
503
|
-
* @param {object} [opts]
|
|
504
|
-
* @returns {Promise<object>}
|
|
505
|
-
*/
|
|
506
|
-
async function douyinUserSearch(apiKey, keyword, opts = {}) {
|
|
507
|
-
const body = { api_key: apiKey, keyword };
|
|
508
|
-
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
509
|
-
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
510
|
-
if (opts.fansRange) body.douyin_user_fans = opts.fansRange;
|
|
511
|
-
if (opts.userType) body.douyin_user_type = opts.userType;
|
|
512
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_SEARCH_PATH, body);
|
|
513
|
-
return stripBilling(resp);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* 搜索抖音视频
|
|
518
|
-
* @param {string} apiKey
|
|
519
|
-
* @param {string} keyword
|
|
520
|
-
* @param {object} [opts]
|
|
521
|
-
* @returns {Promise<object>}
|
|
522
|
-
*/
|
|
523
|
-
async function douyinVideoSearch(apiKey, keyword, opts = {}) {
|
|
524
|
-
const body = { keyword };
|
|
525
|
-
if (opts.sortType !== undefined) body.sort_type = opts.sortType;
|
|
526
|
-
if (opts.publishTime !== undefined) body.publish_time = opts.publishTime;
|
|
527
|
-
if (opts.filterDuration !== undefined) body.filter_duration = opts.filterDuration;
|
|
528
|
-
if (opts.contentType !== undefined) body.content_type = opts.contentType;
|
|
529
|
-
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
530
|
-
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
531
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_SEARCH_PATH, body, {
|
|
532
|
-
Authorization: `Bearer ${apiKey}`,
|
|
533
|
-
});
|
|
534
|
-
return stripBilling(resp);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* 抖音作品链接转文字
|
|
539
|
-
* @param {string} apiKey
|
|
540
|
-
* @param {string} videoUrl
|
|
541
|
-
* @returns {Promise<object>}
|
|
542
|
-
*/
|
|
543
|
-
async function douyinLinkToText(apiKey, videoUrl) {
|
|
544
|
-
const resp = await httpPost(API_HOST, DOUYIN_LINK_TO_TEXT_PATH, {
|
|
545
|
-
api_key: apiKey,
|
|
546
|
-
video_url: videoUrl,
|
|
547
|
-
});
|
|
548
|
-
return stripBilling(resp);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
*
|
|
553
|
-
* @param {string} apiKey
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* 获取抖音作品详情
|
|
490
|
+
* @param {string} apiKey
|
|
491
|
+
* @param {string} shareUrl
|
|
492
|
+
* @returns {Promise<object>}
|
|
493
|
+
*/
|
|
494
|
+
async function douyinVideoInfo(apiKey, shareUrl) {
|
|
495
|
+
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_INFO_PATH, {
|
|
496
|
+
api_key: apiKey,
|
|
497
|
+
share_url: shareUrl,
|
|
498
|
+
});
|
|
499
|
+
return stripBilling(resp);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* 获取抖音作品评论
|
|
504
|
+
* @param {string} apiKey
|
|
505
|
+
* @param {string} videoUrl
|
|
506
|
+
* @param {number} [maxCount=50]
|
|
507
|
+
* @returns {Promise<object>}
|
|
508
|
+
*/
|
|
509
|
+
async function douyinVideoComments(apiKey, videoUrl, maxCount) {
|
|
510
|
+
const body = { api_key: apiKey, video_url: videoUrl };
|
|
511
|
+
if (maxCount !== undefined && maxCount !== null) body.max_count = maxCount;
|
|
512
|
+
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_COMMENTS_PATH, body);
|
|
513
|
+
return stripBilling(resp);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* 获取抖音用户视频列表
|
|
518
|
+
* @param {string} apiKey
|
|
519
|
+
* @param {string} homePage
|
|
520
|
+
* @param {number} [maxSeconds=50]
|
|
521
|
+
* @param {number} [maxPages=1]
|
|
522
|
+
* @returns {Promise<object>}
|
|
523
|
+
*/
|
|
524
|
+
async function douyinUserVideos(apiKey, homePage, maxSeconds, maxPages) {
|
|
525
|
+
const body = { api_key: apiKey, home_page: homePage };
|
|
526
|
+
if (maxSeconds !== undefined && maxSeconds !== null) body.max_seconds = maxSeconds;
|
|
527
|
+
if (maxPages !== undefined && maxPages !== null) body.max_pages = maxPages;
|
|
528
|
+
const resp = await httpPost(API_HOST, DOUYIN_USER_VIDEOS_PATH, body);
|
|
529
|
+
return stripBilling(resp);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* 获取抖音用户详情
|
|
534
|
+
* @param {string} apiKey
|
|
535
|
+
* @param {string} homePage
|
|
536
|
+
* @returns {Promise<object>}
|
|
537
|
+
*/
|
|
538
|
+
async function douyinUserDetail(apiKey, homePage) {
|
|
539
|
+
const resp = await httpPost(API_HOST, DOUYIN_USER_DETAIL_PATH, {
|
|
540
|
+
api_key: apiKey,
|
|
541
|
+
home_page: homePage,
|
|
542
|
+
});
|
|
543
|
+
return stripBilling(resp);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* 搜索抖音用户
|
|
548
|
+
* @param {string} apiKey
|
|
549
|
+
* @param {string} keyword
|
|
550
|
+
* @param {object} [opts]
|
|
551
|
+
* @returns {Promise<object>}
|
|
552
|
+
*/
|
|
553
|
+
async function douyinUserSearch(apiKey, keyword, opts = {}) {
|
|
554
|
+
const body = { api_key: apiKey, keyword };
|
|
555
|
+
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
556
|
+
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
557
|
+
if (opts.fansRange) body.douyin_user_fans = opts.fansRange;
|
|
558
|
+
if (opts.userType) body.douyin_user_type = opts.userType;
|
|
559
|
+
const resp = await httpPost(API_HOST, DOUYIN_USER_SEARCH_PATH, body);
|
|
560
|
+
return stripBilling(resp);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* 搜索抖音视频
|
|
565
|
+
* @param {string} apiKey
|
|
566
|
+
* @param {string} keyword
|
|
567
|
+
* @param {object} [opts]
|
|
568
|
+
* @returns {Promise<object>}
|
|
569
|
+
*/
|
|
570
|
+
async function douyinVideoSearch(apiKey, keyword, opts = {}) {
|
|
571
|
+
const body = { keyword };
|
|
572
|
+
if (opts.sortType !== undefined) body.sort_type = opts.sortType;
|
|
573
|
+
if (opts.publishTime !== undefined) body.publish_time = opts.publishTime;
|
|
574
|
+
if (opts.filterDuration !== undefined) body.filter_duration = opts.filterDuration;
|
|
575
|
+
if (opts.contentType !== undefined) body.content_type = opts.contentType;
|
|
576
|
+
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
577
|
+
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
578
|
+
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_SEARCH_PATH, body, {
|
|
579
|
+
Authorization: `Bearer ${apiKey}`,
|
|
580
|
+
});
|
|
581
|
+
return stripBilling(resp);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* 抖音作品链接转文字
|
|
586
|
+
* @param {string} apiKey
|
|
587
|
+
* @param {string} videoUrl
|
|
588
|
+
* @returns {Promise<object>}
|
|
589
|
+
*/
|
|
590
|
+
async function douyinLinkToText(apiKey, videoUrl) {
|
|
591
|
+
const resp = await httpPost(API_HOST, DOUYIN_LINK_TO_TEXT_PATH, {
|
|
592
|
+
api_key: apiKey,
|
|
593
|
+
video_url: videoUrl,
|
|
594
|
+
});
|
|
595
|
+
return stripBilling(resp);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* 获取微信公众号文章互动数据(按次计费)
|
|
600
|
+
* @param {string} apiKey
|
|
601
|
+
* @param {string} url 微信文章链接
|
|
602
|
+
* @returns {Promise<object>}
|
|
603
|
+
*/
|
|
604
|
+
async function wxArticleData(apiKey, url) {
|
|
605
|
+
const resp = await httpPost(API_HOST, WX_ARTICLE_DATA_PATH, {
|
|
606
|
+
url: requireText(url, "url(微信文章链接)"),
|
|
607
|
+
}, {
|
|
608
|
+
Authorization: `Bearer ${apiKey}`,
|
|
609
|
+
});
|
|
610
|
+
return throwOnBusinessFailure(stripBilling(resp));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* 获取微信公众号历史文章列表(按成功翻页次数计费)
|
|
615
|
+
* @param {string} apiKey
|
|
616
|
+
* @param {{ url?: string, name?: string, maxPages?: number|string, maxSeconds?: number|string }} params
|
|
617
|
+
* @returns {Promise<object>}
|
|
618
|
+
*/
|
|
619
|
+
async function wxHistoryArticles(apiKey, params = {}) {
|
|
620
|
+
const body = {};
|
|
621
|
+
const url = params.url === undefined || params.url === null ? "" : String(params.url).trim();
|
|
622
|
+
const name = params.name === undefined || params.name === null ? "" : String(params.name).trim();
|
|
623
|
+
if (!url && !name) {
|
|
624
|
+
throw Object.assign(
|
|
625
|
+
new Error("url 与 name 至少提供一个(公众号名称,或该公众号任意一篇文章链接)"),
|
|
626
|
+
{ type: "validation_error" }
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
if (url) body.url = url;
|
|
630
|
+
if (name) body.name = name;
|
|
631
|
+
if (params.maxPages !== undefined && params.maxPages !== null) {
|
|
632
|
+
body.max_pages = validatePositiveInt(params.maxPages, "max_pages");
|
|
633
|
+
}
|
|
634
|
+
if (params.maxSeconds !== undefined && params.maxSeconds !== null) {
|
|
635
|
+
body.max_seconds = validatePositiveInt(params.maxSeconds, "max_seconds", WX_HISTORY_MAX_SECONDS);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const resp = await httpPost(API_HOST, WX_HISTORY_ARTICLES_PATH, body, {
|
|
639
|
+
Authorization: `Bearer ${apiKey}`,
|
|
640
|
+
});
|
|
641
|
+
return throwOnBusinessFailure(stripBilling(resp));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* 小红书笔记发布 — 生成扫码发布 H5 页面与二维码
|
|
646
|
+
* @param {string} apiKey
|
|
554
647
|
* @param {{ type: string, title?: string, content?: string, images?: string[], video?: string, cover?: string }} params
|
|
555
|
-
* @returns {Promise<object>}
|
|
556
|
-
*/
|
|
557
|
-
async function xhsNotePublish(apiKey, params) {
|
|
558
|
-
const body = { type: params.type };
|
|
559
|
-
if (params.title) body.title = params.title;
|
|
560
|
-
if (params.content) body.content = params.content;
|
|
648
|
+
* @returns {Promise<object>}
|
|
649
|
+
*/
|
|
650
|
+
async function xhsNotePublish(apiKey, params) {
|
|
651
|
+
const body = { type: params.type };
|
|
652
|
+
if (params.title) body.title = params.title;
|
|
653
|
+
if (params.content) body.content = params.content;
|
|
561
654
|
if (params.images && params.images.length > 0) body.images = params.images;
|
|
562
655
|
if (params.video) body.video = params.video;
|
|
563
656
|
if (params.cover) body.cover = params.cover;
|
|
564
|
-
|
|
565
|
-
const resp = await httpPost(API_HOST, XHS_NOTE_PUBLISH_PATH, body, {
|
|
566
|
-
Authorization: `Bearer ${apiKey}`,
|
|
567
|
-
});
|
|
568
|
-
return stripBilling(resp);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
module.exports = {
|
|
572
|
-
resolveApiKey,
|
|
657
|
+
|
|
658
|
+
const resp = await httpPost(API_HOST, XHS_NOTE_PUBLISH_PATH, body, {
|
|
659
|
+
Authorization: `Bearer ${apiKey}`,
|
|
660
|
+
});
|
|
661
|
+
return stripBilling(resp);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
module.exports = {
|
|
665
|
+
resolveApiKey,
|
|
573
666
|
publishHtml,
|
|
574
667
|
publishMdToHtml,
|
|
575
668
|
getPublishedWebpages,
|
|
576
669
|
deletePublishedWebpage,
|
|
577
670
|
webSearch,
|
|
578
671
|
hotList,
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
672
|
+
xhsHotList,
|
|
673
|
+
douyinVideoInfo,
|
|
674
|
+
douyinVideoComments,
|
|
675
|
+
douyinUserVideos,
|
|
676
|
+
douyinUserDetail,
|
|
677
|
+
douyinUserSearch,
|
|
584
678
|
douyinVideoSearch,
|
|
585
679
|
douyinLinkToText,
|
|
586
680
|
r2Upload,
|
|
587
681
|
xhsNotePublish,
|
|
682
|
+
wxArticleData,
|
|
683
|
+
wxHistoryArticles,
|
|
588
684
|
};
|