aiconductor-cli 1.4.0 → 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 +25 -2
- package/api_docs/douyin_user_videos.md +2 -2
- package/api_docs/wx_history_articles.md +39 -20
- package/bin/aiconductor-cli.js +134 -106
- package/package.json +1 -1
- package/src/api.js +346 -268
- package/src/api.test.js +115 -2
- package/src/commands/wx.js +116 -0
package/src/api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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";
|
|
@@ -11,73 +11,77 @@ 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
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";
|
|
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";
|
|
20
20
|
const DOUYIN_LINK_TO_TEXT_PATH = "/api/douyin_link_to_text_v2";
|
|
21
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";
|
|
22
24
|
const R2_UPLOADER_PATH = "/api/r2_uploader";
|
|
23
25
|
const MAX_R2_FILE_SIZE = 100 * 1024 * 1024;
|
|
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
|
-
*
|
|
67
|
-
|
|
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
|
+
*/
|
|
68
72
|
function validateInt(value, name) {
|
|
69
|
-
if (value === undefined || value === null) return;
|
|
70
|
-
const n = Number(value);
|
|
71
|
-
if (!Number.isInteger(n) || n < 0) {
|
|
72
|
-
throw Object.assign(new Error(`${name} 须为非负整数,收到: ${value}`), { type: "validation_error" });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* HTTP POST 请求封装
|
|
78
|
-
* @param {string} host
|
|
79
|
-
* @param {string} path
|
|
80
|
-
* @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
|
|
81
85
|
* @param {typeof http|typeof https} [transport]
|
|
82
86
|
* @returns {Promise<object>}
|
|
83
87
|
*/
|
|
@@ -95,36 +99,36 @@ function httpPost(host, requestPath, body, headers = {}, transport = https) {
|
|
|
95
99
|
...headers,
|
|
96
100
|
},
|
|
97
101
|
};
|
|
98
|
-
|
|
102
|
+
|
|
99
103
|
const req = transport.request(options, (res) => {
|
|
100
|
-
let chunks = [];
|
|
101
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
102
|
-
res.on("end", () => {
|
|
103
|
-
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
104
|
-
let parsed;
|
|
105
|
-
try {
|
|
106
|
-
parsed = JSON.parse(raw);
|
|
107
|
-
} catch
|
|
108
|
-
parsed = { success: false, message: raw };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (res.statusCode >= 400) {
|
|
112
|
-
reject({
|
|
113
|
-
type: "http_error",
|
|
114
|
-
statusCode: res.statusCode,
|
|
115
|
-
body: parsed,
|
|
116
|
-
});
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
resolve(parsed);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
req.on("error", (err) => {
|
|
125
|
-
reject({ type: "network_error", message: err.message });
|
|
126
|
-
});
|
|
127
|
-
|
|
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
|
+
|
|
128
132
|
req.write(data);
|
|
129
133
|
req.end();
|
|
130
134
|
});
|
|
@@ -146,6 +150,32 @@ function validatePositiveInt(value, name, max) {
|
|
|
146
150
|
return n;
|
|
147
151
|
}
|
|
148
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
|
+
|
|
149
179
|
function contentTypeForFilename(filename) {
|
|
150
180
|
const types = {
|
|
151
181
|
".avif": "image/avif",
|
|
@@ -257,26 +287,26 @@ async function r2Upload(apiKey, params) {
|
|
|
257
287
|
}
|
|
258
288
|
return stripBilling(resp);
|
|
259
289
|
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* 发布 HTML 到线上
|
|
263
|
-
* @param {string} apiKey
|
|
264
|
-
* @param {string} htmlCode
|
|
265
|
-
* @param {string} [title]
|
|
266
|
-
* @returns {Promise<{success: boolean, message: string, online_url: string}>}
|
|
267
|
-
*/
|
|
268
|
-
async function publishHtml(apiKey, htmlCode, title) {
|
|
269
|
-
const reqBody = {
|
|
270
|
-
api_key: apiKey,
|
|
271
|
-
html_code: htmlCode,
|
|
272
|
-
};
|
|
273
|
-
if (title) reqBody.title = title;
|
|
274
|
-
|
|
275
|
-
const resp = await httpPost(API_HOST, API_PATH, reqBody);
|
|
276
|
-
return stripBilling(resp);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
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
|
+
/**
|
|
280
310
|
* 豆包网页/图片搜索
|
|
281
311
|
* @param {string} apiKey
|
|
282
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
|
|
@@ -333,7 +363,7 @@ async function webSearch(apiKey, params) {
|
|
|
333
363
|
if (imageHeightMax !== undefined) reqBody.image_height_max = imageHeightMax;
|
|
334
364
|
if (params.imageShapes !== undefined) reqBody.image_shapes = params.imageShapes;
|
|
335
365
|
}
|
|
336
|
-
|
|
366
|
+
|
|
337
367
|
const resp = await httpPost(API_HOST, SEARCH_API_PATH, reqBody, {
|
|
338
368
|
Authorization: `Bearer ${apiKey}`,
|
|
339
369
|
});
|
|
@@ -397,25 +427,25 @@ async function xhsHotList(apiKey) {
|
|
|
397
427
|
}
|
|
398
428
|
return result;
|
|
399
429
|
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Markdown 转 HTML 并发布
|
|
403
|
-
* @param {string} apiKey
|
|
404
|
-
* @param {string} mdText
|
|
405
|
-
* @param {string} [filename]
|
|
406
|
-
* @param {boolean} [addCopyButton=true]
|
|
407
|
-
* @returns {Promise<{success: boolean, message: string, file_url: string, online_url: string}>}
|
|
408
|
-
*/
|
|
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
|
+
*/
|
|
409
439
|
async function publishMdToHtml(apiKey, mdText, filename, addCopyButton = false) {
|
|
410
|
-
const reqBody = {
|
|
411
|
-
api_key: apiKey,
|
|
412
|
-
md_text: mdText,
|
|
413
|
-
add_copy_button: addCopyButton,
|
|
414
|
-
};
|
|
415
|
-
if (filename) reqBody.filename = filename;
|
|
416
|
-
|
|
417
|
-
const resp = await httpPost(API_HOST, MD_TO_HTML_API_PATH, reqBody);
|
|
418
|
-
|
|
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
|
+
|
|
419
449
|
return stripBilling(resp);
|
|
420
450
|
}
|
|
421
451
|
|
|
@@ -454,139 +484,185 @@ async function deletePublishedWebpage(apiKey, webpageId) {
|
|
|
454
484
|
|
|
455
485
|
return stripBilling(resp);
|
|
456
486
|
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* 获取抖音作品详情
|
|
460
|
-
* @param {string} apiKey
|
|
461
|
-
* @param {string} shareUrl
|
|
462
|
-
* @returns {Promise<object>}
|
|
463
|
-
*/
|
|
464
|
-
async function douyinVideoInfo(apiKey, shareUrl) {
|
|
465
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_INFO_PATH, {
|
|
466
|
-
api_key: apiKey,
|
|
467
|
-
share_url: shareUrl,
|
|
468
|
-
});
|
|
469
|
-
return stripBilling(resp);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* 获取抖音作品评论
|
|
474
|
-
* @param {string} apiKey
|
|
475
|
-
* @param {string} videoUrl
|
|
476
|
-
* @param {number} [maxCount=50]
|
|
477
|
-
* @returns {Promise<object>}
|
|
478
|
-
*/
|
|
479
|
-
async function douyinVideoComments(apiKey, videoUrl, maxCount) {
|
|
480
|
-
const body = { api_key: apiKey, video_url: videoUrl };
|
|
481
|
-
if (maxCount !== undefined && maxCount !== null) body.max_count = maxCount;
|
|
482
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_COMMENTS_PATH, body);
|
|
483
|
-
return stripBilling(resp);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* 获取抖音用户视频列表
|
|
488
|
-
* @param {string} apiKey
|
|
489
|
-
* @param {string} homePage
|
|
490
|
-
* @param {number} [maxSeconds=50]
|
|
491
|
-
* @param {number} [maxPages=1]
|
|
492
|
-
* @returns {Promise<object>}
|
|
493
|
-
*/
|
|
494
|
-
async function douyinUserVideos(apiKey, homePage, maxSeconds, maxPages) {
|
|
495
|
-
const body = { api_key: apiKey, home_page: homePage };
|
|
496
|
-
if (maxSeconds !== undefined && maxSeconds !== null) body.max_seconds = maxSeconds;
|
|
497
|
-
if (maxPages !== undefined && maxPages !== null) body.max_pages = maxPages;
|
|
498
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_VIDEOS_PATH, body);
|
|
499
|
-
return stripBilling(resp);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* 获取抖音用户详情
|
|
504
|
-
* @param {string} apiKey
|
|
505
|
-
* @param {string} homePage
|
|
506
|
-
* @returns {Promise<object>}
|
|
507
|
-
*/
|
|
508
|
-
async function douyinUserDetail(apiKey, homePage) {
|
|
509
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_DETAIL_PATH, {
|
|
510
|
-
api_key: apiKey,
|
|
511
|
-
home_page: homePage,
|
|
512
|
-
});
|
|
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 douyinUserSearch(apiKey, keyword, opts = {}) {
|
|
524
|
-
const body = { api_key: apiKey, keyword };
|
|
525
|
-
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
526
|
-
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
527
|
-
if (opts.fansRange) body.douyin_user_fans = opts.fansRange;
|
|
528
|
-
if (opts.userType) body.douyin_user_type = opts.userType;
|
|
529
|
-
const resp = await httpPost(API_HOST, DOUYIN_USER_SEARCH_PATH, body);
|
|
530
|
-
return stripBilling(resp);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* 搜索抖音视频
|
|
535
|
-
* @param {string} apiKey
|
|
536
|
-
* @param {string} keyword
|
|
537
|
-
* @param {object} [opts]
|
|
538
|
-
* @returns {Promise<object>}
|
|
539
|
-
*/
|
|
540
|
-
async function douyinVideoSearch(apiKey, keyword, opts = {}) {
|
|
541
|
-
const body = { keyword };
|
|
542
|
-
if (opts.sortType !== undefined) body.sort_type = opts.sortType;
|
|
543
|
-
if (opts.publishTime !== undefined) body.publish_time = opts.publishTime;
|
|
544
|
-
if (opts.filterDuration !== undefined) body.filter_duration = opts.filterDuration;
|
|
545
|
-
if (opts.contentType !== undefined) body.content_type = opts.contentType;
|
|
546
|
-
if (opts.maxPages !== undefined && opts.maxPages !== null) body.max_pages = opts.maxPages;
|
|
547
|
-
if (opts.maxSeconds !== undefined && opts.maxSeconds !== null) body.max_seconds = opts.maxSeconds;
|
|
548
|
-
const resp = await httpPost(API_HOST, DOUYIN_VIDEO_SEARCH_PATH, body, {
|
|
549
|
-
Authorization: `Bearer ${apiKey}`,
|
|
550
|
-
});
|
|
551
|
-
return stripBilling(resp);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* 抖音作品链接转文字
|
|
556
|
-
* @param {string} apiKey
|
|
557
|
-
* @param {string} videoUrl
|
|
558
|
-
* @returns {Promise<object>}
|
|
559
|
-
*/
|
|
560
|
-
async function douyinLinkToText(apiKey, videoUrl) {
|
|
561
|
-
const resp = await httpPost(API_HOST, DOUYIN_LINK_TO_TEXT_PATH, {
|
|
562
|
-
api_key: apiKey,
|
|
563
|
-
video_url: videoUrl,
|
|
564
|
-
});
|
|
565
|
-
return stripBilling(resp);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
*
|
|
570
|
-
* @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
|
|
571
647
|
* @param {{ type: string, title?: string, content?: string, images?: string[], video?: string, cover?: string }} params
|
|
572
|
-
* @returns {Promise<object>}
|
|
573
|
-
*/
|
|
574
|
-
async function xhsNotePublish(apiKey, params) {
|
|
575
|
-
const body = { type: params.type };
|
|
576
|
-
if (params.title) body.title = params.title;
|
|
577
|
-
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;
|
|
578
654
|
if (params.images && params.images.length > 0) body.images = params.images;
|
|
579
655
|
if (params.video) body.video = params.video;
|
|
580
656
|
if (params.cover) body.cover = params.cover;
|
|
581
|
-
|
|
582
|
-
const resp = await httpPost(API_HOST, XHS_NOTE_PUBLISH_PATH, body, {
|
|
583
|
-
Authorization: `Bearer ${apiKey}`,
|
|
584
|
-
});
|
|
585
|
-
return stripBilling(resp);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
module.exports = {
|
|
589
|
-
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,
|
|
590
666
|
publishHtml,
|
|
591
667
|
publishMdToHtml,
|
|
592
668
|
getPublishedWebpages,
|
|
@@ -594,13 +670,15 @@ module.exports = {
|
|
|
594
670
|
webSearch,
|
|
595
671
|
hotList,
|
|
596
672
|
xhsHotList,
|
|
597
|
-
douyinVideoInfo,
|
|
598
|
-
douyinVideoComments,
|
|
599
|
-
douyinUserVideos,
|
|
600
|
-
douyinUserDetail,
|
|
601
|
-
douyinUserSearch,
|
|
673
|
+
douyinVideoInfo,
|
|
674
|
+
douyinVideoComments,
|
|
675
|
+
douyinUserVideos,
|
|
676
|
+
douyinUserDetail,
|
|
677
|
+
douyinUserSearch,
|
|
602
678
|
douyinVideoSearch,
|
|
603
679
|
douyinLinkToText,
|
|
604
680
|
r2Upload,
|
|
605
681
|
xhsNotePublish,
|
|
682
|
+
wxArticleData,
|
|
683
|
+
wxHistoryArticles,
|
|
606
684
|
};
|