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/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
- * 解析 API Key,三级优先级:命令行参数 > 系统环境变量 > .env 文件
27
- * @returns {string|null}
28
- */
29
- function resolveApiKey(optionKey) {
30
- // 1. 命令行参数
31
- if (optionKey) return optionKey;
32
-
33
- // 2. 系统环境变量
34
- if (process.env.AICONDUCTOR_API_KEY) return process.env.AICONDUCTOR_API_KEY;
35
-
36
- // 3. 当前目录 .env 文件
37
- try {
38
- const envPath = path.join(process.cwd(), ".env");
39
- if (fs.existsSync(envPath)) {
40
- const content = fs.readFileSync(envPath, "utf-8");
41
- const match = content.match(/^AICONDUCTOR_API_KEY\s*=\s*(.+)$/m);
42
- if (match) return match[1].trim();
43
- }
44
- } catch (_) {
45
- // .env 不可读,忽略
46
- }
47
-
48
- return null;
49
- }
50
-
51
- /**
52
- * 过滤响应中的计费信息
53
- * @param {object} resp
54
- * @returns {object}
55
- */
56
- function stripBilling(resp) {
57
- if (!resp || typeof resp !== "object") return resp;
58
- const result = { ...resp };
59
- delete result.billing;
60
- return result;
61
- }
62
-
63
- /**
64
- * 校验整数参数,非有效正整数时报错
65
- * @param {*} value
66
- * @param {string} name
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
- * 小红书笔记发布 — 生成扫码发布 H5 页面与二维码
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
  };