@iflyrpa/actions 1.1.8 → 1.1.9
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/dist/index.cjs +281 -31
- package/dist/index.mjs +280 -31
- package/package.json +1 -2
- package/src/actions/xiaohongshuPublish/index.ts +7 -9
- package/src/actions/xiaohongshuPublish/mock.ts +40 -38
- package/src/utils/xhsXsEncrypt.ts +103 -0
package/dist/index.cjs
CHANGED
|
@@ -14,6 +14,8 @@ const require$$4$1 = require('assert');
|
|
|
14
14
|
const require$$0$1 = require('tty');
|
|
15
15
|
const zlib = require('zlib');
|
|
16
16
|
const events$1 = require('events');
|
|
17
|
+
const node_buffer = require('node:buffer');
|
|
18
|
+
const crypto = require('node:crypto');
|
|
17
19
|
|
|
18
20
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
19
21
|
|
|
@@ -44,6 +46,7 @@ const require$$6__default = /*#__PURE__*/_interopDefaultCompat(require$$6);
|
|
|
44
46
|
const require$$4__default$1 = /*#__PURE__*/_interopDefaultCompat(require$$4$1);
|
|
45
47
|
const require$$0__default$1 = /*#__PURE__*/_interopDefaultCompat(require$$0$1);
|
|
46
48
|
const zlib__default = /*#__PURE__*/_interopDefaultCompat(zlib);
|
|
49
|
+
const crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
|
|
47
50
|
|
|
48
51
|
function ensureFile(filePath) {
|
|
49
52
|
return new Promise((resolve, reject)=>{
|
|
@@ -967,8 +970,6 @@ AxiosError.from = (error, code, config, request, response, customProps) => {
|
|
|
967
970
|
return axiosError;
|
|
968
971
|
};
|
|
969
972
|
|
|
970
|
-
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
971
|
-
|
|
972
973
|
function getDefaultExportFromCjs (x) {
|
|
973
974
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
974
975
|
}
|
|
@@ -21627,7 +21628,7 @@ const replaceImgSrc = (html, callback) => {
|
|
|
21627
21628
|
return lastedHtml;
|
|
21628
21629
|
};
|
|
21629
21630
|
|
|
21630
|
-
const errnoMap$
|
|
21631
|
+
const errnoMap$3 = {
|
|
21631
21632
|
20040706: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21632
21633
|
20040084: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21633
21634
|
20050004: "\u4ECA\u65E5\u767E\u5BB6\u53F7\u53D1\u5E03\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u660E\u5929\u518D\u8BD5\u3002",
|
|
@@ -21639,7 +21640,7 @@ const errnoMap$2 = {
|
|
|
21639
21640
|
2004005714: "\u7F51\u7EDC\u73AF\u5883\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u53D1\u5E03\u3002",
|
|
21640
21641
|
401100032: "\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u56FE\u7247\uFF08\u5982\uFF1A\u683C\u5F0F\u3001\u7248\u6743\u3001\u5408\u89C4\u6027\u7B49\uFF09\u3002"
|
|
21641
21642
|
};
|
|
21642
|
-
const mockAction$
|
|
21643
|
+
const mockAction$5 = async (task, params) => {
|
|
21643
21644
|
const { baijiahaoSingleCover, baijiahaoMultCover, baijiahaoCoverType } = params.settingInfo;
|
|
21644
21645
|
const tmpCachePath = task.getTmpPath();
|
|
21645
21646
|
const api = axios$1.create({
|
|
@@ -21651,7 +21652,7 @@ const mockAction$4 = async (task, params) => {
|
|
|
21651
21652
|
api.interceptors.response.use(
|
|
21652
21653
|
(response) => {
|
|
21653
21654
|
if (response.data.errno !== 0) {
|
|
21654
|
-
const errmsg = response.data.errno === 2005e4 ? (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$
|
|
21655
|
+
const errmsg = response.data.errno === 2005e4 ? (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$3[response.data.errno] : response.data.errno === 401100032 ? "\u56FE\u7247" + (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$3[response.data.errno] : errnoMap$3[response.data.errno] || response.config.defaultErrorMsg || (params.saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21655
21656
|
task.logger.error(errmsg);
|
|
21656
21657
|
return Promise.reject(new Error(errmsg));
|
|
21657
21658
|
}
|
|
@@ -21770,7 +21771,7 @@ const mockAction$4 = async (task, params) => {
|
|
|
21770
21771
|
|
|
21771
21772
|
const baijiahaoPublish = async (task, params) => {
|
|
21772
21773
|
task.logger.info("\u5F00\u59CB\u767E\u5BB6\u53F7\u53D1\u5E03");
|
|
21773
|
-
return mockAction$
|
|
21774
|
+
return mockAction$5(task, params);
|
|
21774
21775
|
};
|
|
21775
21776
|
|
|
21776
21777
|
const getBaijiahaoActivity = async (task, params) => {
|
|
@@ -21900,7 +21901,7 @@ const COVER_TYPE = {
|
|
|
21900
21901
|
single: 2,
|
|
21901
21902
|
multiple: 3
|
|
21902
21903
|
};
|
|
21903
|
-
const errnoMap$
|
|
21904
|
+
const errnoMap$2 = {
|
|
21904
21905
|
20004020: "\u56FE\u7247\u4E0A\u4F20\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u8D26\u53F7\u540E\u540C\u6B65\u3002",
|
|
21905
21906
|
7115: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21906
21907
|
100006: "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u8D26\u53F7\u540E\u91CD\u8BD5\u3002",
|
|
@@ -21924,7 +21925,7 @@ const get3101DetailError = (errorList, message, saveType) => {
|
|
|
21924
21925
|
}
|
|
21925
21926
|
return error || (saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21926
21927
|
};
|
|
21927
|
-
const mockAction$
|
|
21928
|
+
const mockAction$4 = async (task, params) => {
|
|
21928
21929
|
const { toutiaoSingleCover, toutiaoMultCover, toutiaoCoverType, toutiaoOriginal } = params.settingInfo;
|
|
21929
21930
|
const tmpCachePath = task.getTmpPath();
|
|
21930
21931
|
const api = axios$1.create({
|
|
@@ -21938,10 +21939,10 @@ const mockAction$3 = async (task, params) => {
|
|
|
21938
21939
|
(response) => {
|
|
21939
21940
|
if (response.data.code !== 0) {
|
|
21940
21941
|
let errmsg = "";
|
|
21941
|
-
if (Array.isArray(errnoMap$
|
|
21942
|
-
errmsg = get3101DetailError(errnoMap$
|
|
21942
|
+
if (Array.isArray(errnoMap$2[response.data.code])) {
|
|
21943
|
+
errmsg = get3101DetailError(errnoMap$2[response.data.code], response.data.message, params.saveType);
|
|
21943
21944
|
} else {
|
|
21944
|
-
errmsg = errnoMap$
|
|
21945
|
+
errmsg = errnoMap$2[response.data.code] || response.config.defaultErrorMsg || (params.saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21945
21946
|
}
|
|
21946
21947
|
task.logger.error(errmsg);
|
|
21947
21948
|
return Promise.reject(new Error(errmsg));
|
|
@@ -22089,10 +22090,10 @@ const mockAction$3 = async (task, params) => {
|
|
|
22089
22090
|
|
|
22090
22091
|
const toutiaoPublish = async (task, params) => {
|
|
22091
22092
|
task.logger.info("\u5F00\u59CB\u5934\u6761\u53D1\u5E03");
|
|
22092
|
-
return mockAction$
|
|
22093
|
+
return mockAction$4(task, params);
|
|
22093
22094
|
};
|
|
22094
22095
|
|
|
22095
|
-
const mockAction$
|
|
22096
|
+
const mockAction$3 = async (task, params) => {
|
|
22096
22097
|
const tmpCachePath = task.getTmpPath();
|
|
22097
22098
|
const api = axios$1.create({
|
|
22098
22099
|
headers: {
|
|
@@ -22223,9 +22224,9 @@ const rpaAction$2 = async (task, params) => {
|
|
|
22223
22224
|
await page.locator(".footer-wrap button.publish-content").click();
|
|
22224
22225
|
};
|
|
22225
22226
|
|
|
22226
|
-
const executeAction$
|
|
22227
|
+
const executeAction$2 = (task, params) => {
|
|
22227
22228
|
return new Promise((resolve, reject) => {
|
|
22228
|
-
mockAction$
|
|
22229
|
+
mockAction$3(task, params).then(resolve).catch(() => {
|
|
22229
22230
|
rpaAction$2(task, params).then(resolve).catch(reject);
|
|
22230
22231
|
});
|
|
22231
22232
|
});
|
|
@@ -22236,13 +22237,13 @@ const weitoutiaoPublish = async (task, params) => {
|
|
|
22236
22237
|
return rpaAction$2(task, params);
|
|
22237
22238
|
}
|
|
22238
22239
|
if (params.actionType === "mockApi") {
|
|
22239
|
-
return mockAction$
|
|
22240
|
+
return mockAction$3(task, params);
|
|
22240
22241
|
}
|
|
22241
|
-
return executeAction$
|
|
22242
|
+
return executeAction$2(task, params);
|
|
22242
22243
|
};
|
|
22243
22244
|
|
|
22244
22245
|
const scanRetryMaxCount = 30;
|
|
22245
|
-
const errnoMap = {
|
|
22246
|
+
const errnoMap$1 = {
|
|
22246
22247
|
200003: "\u767B\u5F55\u5931\u6548"
|
|
22247
22248
|
};
|
|
22248
22249
|
const saveDraft$1 = (api, params, data) => {
|
|
@@ -22340,7 +22341,7 @@ const massSend = async (api, params, data) => {
|
|
|
22340
22341
|
});
|
|
22341
22342
|
return res;
|
|
22342
22343
|
};
|
|
22343
|
-
const mockAction$
|
|
22344
|
+
const mockAction$2 = async (task, params) => {
|
|
22344
22345
|
const tmpCachePath = task.getTmpPath();
|
|
22345
22346
|
const api = axios$1.create({
|
|
22346
22347
|
headers: {
|
|
@@ -22352,7 +22353,7 @@ const mockAction$1 = async (task, params) => {
|
|
|
22352
22353
|
(response) => {
|
|
22353
22354
|
const responseData = response.data;
|
|
22354
22355
|
if (responseData?.base_resp && responseData.base_resp.ret !== 0) {
|
|
22355
|
-
const errmsg = errnoMap[responseData.base_resp.ret] || response.config.defaultErrorMsg || responseData.base_resp.err_msg || "Unknown error";
|
|
22356
|
+
const errmsg = errnoMap$1[responseData.base_resp.ret] || response.config.defaultErrorMsg || responseData.base_resp.err_msg || "Unknown error";
|
|
22356
22357
|
task.logger.error(errmsg, responseData);
|
|
22357
22358
|
return Promise.reject(new Error(errmsg));
|
|
22358
22359
|
}
|
|
@@ -22561,7 +22562,7 @@ const mockAction$1 = async (task, params) => {
|
|
|
22561
22562
|
|
|
22562
22563
|
const weixinPublish = async (task, params) => {
|
|
22563
22564
|
task.logger.info("\u5F00\u59CB\u5FAE\u4FE1\u516C\u4F17\u53F7\u53D1\u5E03");
|
|
22564
|
-
return mockAction$
|
|
22565
|
+
return mockAction$2(task, params);
|
|
22565
22566
|
};
|
|
22566
22567
|
|
|
22567
22568
|
const saveDraft = (api, params, data) => {
|
|
@@ -22582,7 +22583,7 @@ const saveDraft = (api, params, data) => {
|
|
|
22582
22583
|
data: formData
|
|
22583
22584
|
});
|
|
22584
22585
|
};
|
|
22585
|
-
const mockAction = async (task, params) => {
|
|
22586
|
+
const mockAction$1 = async (task, params) => {
|
|
22586
22587
|
const tmpCachePath = task.getTmpPath();
|
|
22587
22588
|
const api = axios$1.create({
|
|
22588
22589
|
headers: {
|
|
@@ -22763,9 +22764,9 @@ const rpaAction$1 = async (task, params) => {
|
|
|
22763
22764
|
}
|
|
22764
22765
|
};
|
|
22765
22766
|
|
|
22766
|
-
const executeAction = (task, params) => {
|
|
22767
|
+
const executeAction$1 = (task, params) => {
|
|
22767
22768
|
return new Promise((resolve, reject) => {
|
|
22768
|
-
mockAction(task, params).then(resolve).catch(() => {
|
|
22769
|
+
mockAction$1(task, params).then(resolve).catch(() => {
|
|
22769
22770
|
rpaAction$1(task, params).then(resolve).catch(reject);
|
|
22770
22771
|
});
|
|
22771
22772
|
});
|
|
@@ -22776,16 +22777,252 @@ const weixinmpPublish = async (task, params) => {
|
|
|
22776
22777
|
return rpaAction$1(task, params);
|
|
22777
22778
|
}
|
|
22778
22779
|
if (params.actionType === "mockApi") {
|
|
22779
|
-
return mockAction(task, params);
|
|
22780
|
+
return mockAction$1(task, params);
|
|
22780
22781
|
}
|
|
22781
|
-
return executeAction(task, params);
|
|
22782
|
+
return executeAction$1(task, params);
|
|
22782
22783
|
};
|
|
22783
22784
|
|
|
22784
|
-
var
|
|
22785
|
+
var __defProp = Object.defineProperty;
|
|
22786
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
22787
|
+
var __publicField = (obj, key, value) => {
|
|
22788
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22789
|
+
return value;
|
|
22790
|
+
};
|
|
22791
|
+
class XsEncrypt {
|
|
22792
|
+
constructor() {
|
|
22793
|
+
__publicField(this, "words", [929260340, 1633971297, 895580464, 925905270]);
|
|
22794
|
+
__publicField(this, "keyBytes", node_buffer.Buffer.concat(
|
|
22795
|
+
this.words.map(
|
|
22796
|
+
(word) => new Uint8Array(node_buffer.Buffer.from([word >> 24 & 255, word >> 16 & 255, word >> 8 & 255, word & 255]))
|
|
22797
|
+
)
|
|
22798
|
+
));
|
|
22799
|
+
__publicField(this, "iv", node_buffer.Buffer.from("4uzjr7mbsibcaldp", "utf8"));
|
|
22800
|
+
}
|
|
22801
|
+
/**
|
|
22802
|
+
* 根据传入的url生成MD5摘要
|
|
22803
|
+
*
|
|
22804
|
+
* @param {string} url - API的url
|
|
22805
|
+
* @returns {string} MD5摘要
|
|
22806
|
+
*/
|
|
22807
|
+
async encryptMD5(url) {
|
|
22808
|
+
return crypto__default.createHash("md5").update(url, "utf8").digest("hex");
|
|
22809
|
+
}
|
|
22810
|
+
/**
|
|
22811
|
+
* 根据传入的text生成AES加密后的内容,并将其转为base64编码
|
|
22812
|
+
*
|
|
22813
|
+
* @param {string} text - 需要加密的字符串
|
|
22814
|
+
* @returns {string} 加密后的base64编码字符串
|
|
22815
|
+
*/
|
|
22816
|
+
async encryptText(text) {
|
|
22817
|
+
const textEncoded = node_buffer.Buffer.from(text).toString("base64");
|
|
22818
|
+
const cipher = crypto__default.createCipheriv("aes-128-cbc", new Uint8Array(this.keyBytes), new Uint8Array(this.iv));
|
|
22819
|
+
const ciphertext = cipher.update(textEncoded, "utf8", "base64");
|
|
22820
|
+
return ciphertext + cipher.final("base64");
|
|
22821
|
+
}
|
|
22822
|
+
/**
|
|
22823
|
+
* 把加密后的payload转为16进制
|
|
22824
|
+
*
|
|
22825
|
+
* @param {string} encodedData - 加密后的payload(Base64编码)
|
|
22826
|
+
* @returns {string} 十六进制字符串
|
|
22827
|
+
*/
|
|
22828
|
+
async base64ToHex(encodedData) {
|
|
22829
|
+
const decodedData = node_buffer.Buffer.from(encodedData, "base64");
|
|
22830
|
+
return decodedData.toString("hex");
|
|
22831
|
+
}
|
|
22832
|
+
/**
|
|
22833
|
+
* 把小红书加密参数payload转16进制 再使用base64编码
|
|
22834
|
+
*
|
|
22835
|
+
* @param {string} payload - 要加密处理的payload内容
|
|
22836
|
+
* @param {string} platform - 登录平台
|
|
22837
|
+
* @returns {string} 加密后并进行base64编码的字符串
|
|
22838
|
+
*/
|
|
22839
|
+
async encryptPayload(payload, platform) {
|
|
22840
|
+
const hexPayload = await this.base64ToHex(payload);
|
|
22841
|
+
const obj = {
|
|
22842
|
+
signSvn: "56",
|
|
22843
|
+
signType: "x2",
|
|
22844
|
+
appID: platform,
|
|
22845
|
+
signVersion: "1",
|
|
22846
|
+
payload: hexPayload
|
|
22847
|
+
};
|
|
22848
|
+
const jsonString = JSON.stringify(obj, null, 0);
|
|
22849
|
+
return node_buffer.Buffer.from(jsonString).toString("base64");
|
|
22850
|
+
}
|
|
22851
|
+
/**
|
|
22852
|
+
* 将传入的参数加密为小红书的xs
|
|
22853
|
+
* Args: url: API请求的URL
|
|
22854
|
+
* a1: 签名参数a1
|
|
22855
|
+
* ts: 时间戳
|
|
22856
|
+
* platform: 登录平台 默认为xhs-pc-web
|
|
22857
|
+
* Returns:
|
|
22858
|
+
* 最终的加密签名字符串,前缀为“XYW_”
|
|
22859
|
+
*/
|
|
22860
|
+
async encryptXs(url, a1, ts, platform = "xhs-pc-web") {
|
|
22861
|
+
const text = `x1=${await this.encryptMD5(`url=${url}`)};x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0|1|0|0|0;x3=${a1};x4=${ts};`;
|
|
22862
|
+
return `XYW_${await this.encryptPayload(await this.encryptText(text), platform)}`;
|
|
22863
|
+
}
|
|
22864
|
+
dumps(...rest) {
|
|
22865
|
+
const [data, replacer = null, space = 0] = rest;
|
|
22866
|
+
return JSON.stringify(data, replacer, space).replace(/\n/g, "").replace(/":\s+"/g, '":"');
|
|
22867
|
+
}
|
|
22868
|
+
}
|
|
22785
22869
|
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22870
|
+
const errnoMap = {};
|
|
22871
|
+
const xsEncrypt = new XsEncrypt();
|
|
22872
|
+
const mockAction = async (task, params) => {
|
|
22873
|
+
const tmpCachePath = task.getTmpPath();
|
|
22874
|
+
const a1Cookie = params.cookies.find((it) => it.name === "a1")?.value;
|
|
22875
|
+
if (!a1Cookie) {
|
|
22876
|
+
throw new Error("\u672A\u83B7\u53D6\u5230 a1 cookie");
|
|
22877
|
+
}
|
|
22878
|
+
const api = axios$1.create({
|
|
22879
|
+
headers: {
|
|
22880
|
+
cookie: params.cookies.map((it) => `${it.name}=${it.value}`).join(";"),
|
|
22881
|
+
origin: "https://creator.xiaohongshu.com",
|
|
22882
|
+
referer: "https://creator.xiaohongshu.com/",
|
|
22883
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
|
22884
|
+
}
|
|
22885
|
+
});
|
|
22886
|
+
api.interceptors.response.use(
|
|
22887
|
+
(response) => {
|
|
22888
|
+
const responseData = response.data;
|
|
22889
|
+
if (responseData?.code && responseData.code !== 0) {
|
|
22890
|
+
const errmsg = errnoMap[responseData.code] || response.config.defaultErrorMsg || "Unknown error";
|
|
22891
|
+
task.logger.error(errmsg, responseData);
|
|
22892
|
+
return Promise.reject(new Error(errmsg));
|
|
22893
|
+
}
|
|
22894
|
+
return response;
|
|
22895
|
+
},
|
|
22896
|
+
(error) => {
|
|
22897
|
+
task.logger.error("\u5C0F\u7EA2\u4E66\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25", error);
|
|
22898
|
+
return Promise.reject(error);
|
|
22899
|
+
}
|
|
22900
|
+
);
|
|
22901
|
+
const fetchCoverUrl = `/api/media/v1/upload/creator/permit?biz_name=spectrum&scene=image&file_count=${params.banners.length}&version=1&source=web`;
|
|
22902
|
+
const xt = Date.now().toString();
|
|
22903
|
+
const xs = await xsEncrypt.encryptXs(fetchCoverUrl, a1Cookie, xt);
|
|
22904
|
+
const coverIdInfo = await api({
|
|
22905
|
+
method: "get",
|
|
22906
|
+
baseURL: "https://creator.xiaohongshu.com",
|
|
22907
|
+
url: fetchCoverUrl,
|
|
22908
|
+
defaultErrorMsg: "\u83B7\u53D6\u4E0A\u4F20\u9644\u4EF6 token \u5931\u8D25",
|
|
22909
|
+
headers: { "x-s": xs, "x-t": xt }
|
|
22910
|
+
});
|
|
22911
|
+
const coverIds = coverIdInfo.data.data.uploadTempPermits[0].fileIds;
|
|
22912
|
+
const ossToken = coverIdInfo.data.data.uploadTempPermits[0].token;
|
|
22913
|
+
const uploadFile = async (url, index) => {
|
|
22914
|
+
const fileName = getFilenameFromUrl(url);
|
|
22915
|
+
const localUrl = await downloadImage(url, path__default.join(tmpCachePath, fileName));
|
|
22916
|
+
const ossFileId = coverIds[index];
|
|
22917
|
+
const fileBuffer = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c____default.readFileSync(localUrl);
|
|
22918
|
+
await api.put(`https://ros-upload.xiaohongshu.com/${ossFileId}`, fileBuffer, {
|
|
22919
|
+
headers: {
|
|
22920
|
+
"x-cos-security-token": ossToken
|
|
22921
|
+
},
|
|
22922
|
+
defaultErrorMsg: "\u56FE\u7247\u4E0A\u4F20\u51FA\u73B0\u95EE\u9898\uFF0C\u8BF7\u8054\u7CFB\u5BA2\u670D"
|
|
22923
|
+
});
|
|
22924
|
+
return { ossFileId, ossToken };
|
|
22925
|
+
};
|
|
22926
|
+
const coverInfos = await Promise.all(params.banners.map((it, idx) => uploadFile(it, idx)));
|
|
22927
|
+
const publishData = {
|
|
22928
|
+
common: {
|
|
22929
|
+
ats: [],
|
|
22930
|
+
biz_relations: [],
|
|
22931
|
+
desc: params?.content,
|
|
22932
|
+
goods_info: {},
|
|
22933
|
+
hash_tag: [],
|
|
22934
|
+
note_id: "",
|
|
22935
|
+
source: JSON.stringify({
|
|
22936
|
+
type: "web",
|
|
22937
|
+
ids: "",
|
|
22938
|
+
extraInfo: '{"systemId":"web"}'
|
|
22939
|
+
}),
|
|
22940
|
+
title: params?.title,
|
|
22941
|
+
type: "normal",
|
|
22942
|
+
privacy_info: {
|
|
22943
|
+
op_type: 1,
|
|
22944
|
+
type: params.visibleRange === "public" ? 0 : 1
|
|
22945
|
+
},
|
|
22946
|
+
post_loc: params.address ? {
|
|
22947
|
+
name: params.address?.name,
|
|
22948
|
+
poi_id: params.address?.poi_id,
|
|
22949
|
+
poi_type: params.address?.poi_type,
|
|
22950
|
+
subname: params.address?.full_address
|
|
22951
|
+
} : null,
|
|
22952
|
+
business_binds: ""
|
|
22953
|
+
},
|
|
22954
|
+
image_info: {
|
|
22955
|
+
images: coverInfos.map((it) => ({
|
|
22956
|
+
extra_info_json: '{"mimeType":"image/png"}',
|
|
22957
|
+
file_id: it.ossFileId,
|
|
22958
|
+
metadata: { source: -1 },
|
|
22959
|
+
stickers: { floating: [], version: 2 }
|
|
22960
|
+
}))
|
|
22961
|
+
},
|
|
22962
|
+
video_info: null
|
|
22963
|
+
};
|
|
22964
|
+
const userDeclarationBind = {
|
|
22965
|
+
origin: 2,
|
|
22966
|
+
photoInfo: {},
|
|
22967
|
+
repostInfo: {}
|
|
22968
|
+
};
|
|
22969
|
+
if (params.selfDeclaration?.type === "fictional-rendition") {
|
|
22970
|
+
userDeclarationBind.origin = 1;
|
|
22971
|
+
} else if (params.selfDeclaration?.type === "ai-generated") {
|
|
22972
|
+
userDeclarationBind.origin = 2;
|
|
22973
|
+
} else if (params.selfDeclaration?.type === "source-statement") {
|
|
22974
|
+
if (params.selfDeclaration.childType === "self-labeling") {
|
|
22975
|
+
userDeclarationBind.origin = 3;
|
|
22976
|
+
} else if (params.selfDeclaration.childType === "self-shooting") {
|
|
22977
|
+
userDeclarationBind.origin = 4;
|
|
22978
|
+
const photoInfo = {};
|
|
22979
|
+
if (params.selfDeclaration.shootingLocation) {
|
|
22980
|
+
photoInfo.photoPlace = {
|
|
22981
|
+
name: params.selfDeclaration.shootingLocation.name,
|
|
22982
|
+
poiId: params.selfDeclaration.shootingLocation.poi_id,
|
|
22983
|
+
poiType: params.selfDeclaration.shootingLocation.poi_type,
|
|
22984
|
+
subname: params.selfDeclaration.shootingLocation.full_address
|
|
22985
|
+
};
|
|
22986
|
+
}
|
|
22987
|
+
if (params.selfDeclaration.shootingDate) {
|
|
22988
|
+
photoInfo.photoTime = params.selfDeclaration.shootingDate;
|
|
22989
|
+
}
|
|
22990
|
+
userDeclarationBind.photoInfo = photoInfo;
|
|
22991
|
+
} else if (params.selfDeclaration.childType === "transshipment") {
|
|
22992
|
+
userDeclarationBind.origin = 5;
|
|
22993
|
+
if (params.selfDeclaration.sourceMedia) {
|
|
22994
|
+
userDeclarationBind.repostInfo = {
|
|
22995
|
+
source: params.selfDeclaration.sourceMedia
|
|
22996
|
+
};
|
|
22997
|
+
}
|
|
22998
|
+
}
|
|
22999
|
+
}
|
|
23000
|
+
const business_binds = {
|
|
23001
|
+
version: 1,
|
|
23002
|
+
bizType: "",
|
|
23003
|
+
noteId: "",
|
|
23004
|
+
noteOrderBind: {},
|
|
23005
|
+
// 发布时间 - 立即发布/定时发布
|
|
23006
|
+
notePostTiming: params.isImmediatelyPublish ? {} : { postTime: params.scheduledPublish },
|
|
23007
|
+
noteCollectionBind: { id: "" },
|
|
23008
|
+
...params.selfDeclaration ? { userDeclarationBind } : {}
|
|
23009
|
+
};
|
|
23010
|
+
publishData.common.business_binds = JSON.stringify(business_binds);
|
|
23011
|
+
const publishDataStr = xsEncrypt.dumps(publishData);
|
|
23012
|
+
const publishXt = Date.now().toString();
|
|
23013
|
+
const publishXs = await xsEncrypt.encryptXs(`/web_api/sns/v2/note${publishDataStr}`, a1Cookie, publishXt);
|
|
23014
|
+
const publishResult = await api({
|
|
23015
|
+
method: "post",
|
|
23016
|
+
url: "https://edith.xiaohongshu.com/web_api/sns/v2/note",
|
|
23017
|
+
data: publishData,
|
|
23018
|
+
headers: {
|
|
23019
|
+
"x-s": publishXs,
|
|
23020
|
+
"x-t": publishXt
|
|
23021
|
+
},
|
|
23022
|
+
defaultErrorMsg: "\u6587\u7AE0\u53D1\u5E03\u51FA\u73B0\u95EE\u9898\uFF0C\u8BF7\u8054\u7CFB\u5BA2\u670D"
|
|
23023
|
+
});
|
|
23024
|
+
return publishResult.data.data.id;
|
|
23025
|
+
};
|
|
22789
23026
|
|
|
22790
23027
|
const rpaAction = async (task, params) => {
|
|
22791
23028
|
const commonCookies = {
|
|
@@ -22934,9 +23171,22 @@ const rpaAction = async (task, params) => {
|
|
|
22934
23171
|
return response;
|
|
22935
23172
|
};
|
|
22936
23173
|
|
|
23174
|
+
const executeAction = (task, params) => {
|
|
23175
|
+
return new Promise((resolve, reject) => {
|
|
23176
|
+
mockAction(task, params).then(resolve).catch(() => {
|
|
23177
|
+
rpaAction(task, params).then(resolve).catch(reject);
|
|
23178
|
+
});
|
|
23179
|
+
});
|
|
23180
|
+
};
|
|
22937
23181
|
const xiaohongshuPublish = async (task, params) => {
|
|
22938
23182
|
task.logger.info("\u5F00\u59CB\u5C0F\u7EA2\u4E66\u53D1\u5E03");
|
|
22939
|
-
|
|
23183
|
+
if (params.actionType === "rpa") {
|
|
23184
|
+
return rpaAction(task, params);
|
|
23185
|
+
}
|
|
23186
|
+
if (params.actionType === "mockApi") {
|
|
23187
|
+
return mockAction(task, params);
|
|
23188
|
+
}
|
|
23189
|
+
return executeAction(task, params);
|
|
22940
23190
|
};
|
|
22941
23191
|
|
|
22942
23192
|
class Action {
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,8 @@ import require$$4$1 from 'assert';
|
|
|
14
14
|
import require$$0$1 from 'tty';
|
|
15
15
|
import zlib from 'zlib';
|
|
16
16
|
import { EventEmitter } from 'events';
|
|
17
|
+
import { Buffer as Buffer$1 } from 'node:buffer';
|
|
18
|
+
import crypto from 'node:crypto';
|
|
17
19
|
|
|
18
20
|
function ensureFile(filePath) {
|
|
19
21
|
return new Promise((resolve, reject)=>{
|
|
@@ -937,8 +939,6 @@ AxiosError.from = (error, code, config, request, response, customProps) => {
|
|
|
937
939
|
return axiosError;
|
|
938
940
|
};
|
|
939
941
|
|
|
940
|
-
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
941
|
-
|
|
942
942
|
function getDefaultExportFromCjs (x) {
|
|
943
943
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
944
944
|
}
|
|
@@ -21597,7 +21597,7 @@ const replaceImgSrc = (html, callback) => {
|
|
|
21597
21597
|
return lastedHtml;
|
|
21598
21598
|
};
|
|
21599
21599
|
|
|
21600
|
-
const errnoMap$
|
|
21600
|
+
const errnoMap$3 = {
|
|
21601
21601
|
20040706: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21602
21602
|
20040084: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21603
21603
|
20050004: "\u4ECA\u65E5\u767E\u5BB6\u53F7\u53D1\u5E03\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u660E\u5929\u518D\u8BD5\u3002",
|
|
@@ -21609,7 +21609,7 @@ const errnoMap$2 = {
|
|
|
21609
21609
|
2004005714: "\u7F51\u7EDC\u73AF\u5883\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u53D1\u5E03\u3002",
|
|
21610
21610
|
401100032: "\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u56FE\u7247\uFF08\u5982\uFF1A\u683C\u5F0F\u3001\u7248\u6743\u3001\u5408\u89C4\u6027\u7B49\uFF09\u3002"
|
|
21611
21611
|
};
|
|
21612
|
-
const mockAction$
|
|
21612
|
+
const mockAction$5 = async (task, params) => {
|
|
21613
21613
|
const { baijiahaoSingleCover, baijiahaoMultCover, baijiahaoCoverType } = params.settingInfo;
|
|
21614
21614
|
const tmpCachePath = task.getTmpPath();
|
|
21615
21615
|
const api = axios$1.create({
|
|
@@ -21621,7 +21621,7 @@ const mockAction$4 = async (task, params) => {
|
|
|
21621
21621
|
api.interceptors.response.use(
|
|
21622
21622
|
(response) => {
|
|
21623
21623
|
if (response.data.errno !== 0) {
|
|
21624
|
-
const errmsg = response.data.errno === 2005e4 ? (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$
|
|
21624
|
+
const errmsg = response.data.errno === 2005e4 ? (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$3[response.data.errno] : response.data.errno === 401100032 ? "\u56FE\u7247" + (params.saveType === "draft" ? "\u540C\u6B65" : "\u53D1\u5E03") + errnoMap$3[response.data.errno] : errnoMap$3[response.data.errno] || response.config.defaultErrorMsg || (params.saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21625
21625
|
task.logger.error(errmsg);
|
|
21626
21626
|
return Promise.reject(new Error(errmsg));
|
|
21627
21627
|
}
|
|
@@ -21740,7 +21740,7 @@ const mockAction$4 = async (task, params) => {
|
|
|
21740
21740
|
|
|
21741
21741
|
const baijiahaoPublish = async (task, params) => {
|
|
21742
21742
|
task.logger.info("\u5F00\u59CB\u767E\u5BB6\u53F7\u53D1\u5E03");
|
|
21743
|
-
return mockAction$
|
|
21743
|
+
return mockAction$5(task, params);
|
|
21744
21744
|
};
|
|
21745
21745
|
|
|
21746
21746
|
const getBaijiahaoActivity = async (task, params) => {
|
|
@@ -21870,7 +21870,7 @@ const COVER_TYPE = {
|
|
|
21870
21870
|
single: 2,
|
|
21871
21871
|
multiple: 3
|
|
21872
21872
|
};
|
|
21873
|
-
const errnoMap$
|
|
21873
|
+
const errnoMap$2 = {
|
|
21874
21874
|
20004020: "\u56FE\u7247\u4E0A\u4F20\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u8D26\u53F7\u540E\u540C\u6B65\u3002",
|
|
21875
21875
|
7115: "\u6B63\u6587\u56FE\u7247\u548C\u5C01\u9762\u56FE\u7247\u63A8\u8350jpg\u3001png\u683C\u5F0F\u3002",
|
|
21876
21876
|
100006: "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u8D26\u53F7\u540E\u91CD\u8BD5\u3002",
|
|
@@ -21894,7 +21894,7 @@ const get3101DetailError = (errorList, message, saveType) => {
|
|
|
21894
21894
|
}
|
|
21895
21895
|
return error || (saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21896
21896
|
};
|
|
21897
|
-
const mockAction$
|
|
21897
|
+
const mockAction$4 = async (task, params) => {
|
|
21898
21898
|
const { toutiaoSingleCover, toutiaoMultCover, toutiaoCoverType, toutiaoOriginal } = params.settingInfo;
|
|
21899
21899
|
const tmpCachePath = task.getTmpPath();
|
|
21900
21900
|
const api = axios$1.create({
|
|
@@ -21908,10 +21908,10 @@ const mockAction$3 = async (task, params) => {
|
|
|
21908
21908
|
(response) => {
|
|
21909
21909
|
if (response.data.code !== 0) {
|
|
21910
21910
|
let errmsg = "";
|
|
21911
|
-
if (Array.isArray(errnoMap$
|
|
21912
|
-
errmsg = get3101DetailError(errnoMap$
|
|
21911
|
+
if (Array.isArray(errnoMap$2[response.data.code])) {
|
|
21912
|
+
errmsg = get3101DetailError(errnoMap$2[response.data.code], response.data.message, params.saveType);
|
|
21913
21913
|
} else {
|
|
21914
|
-
errmsg = errnoMap$
|
|
21914
|
+
errmsg = errnoMap$2[response.data.code] || response.config.defaultErrorMsg || (params.saveType === "draft" ? "\u6587\u7AE0\u540C\u6B65\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : "\u6587\u7AE0\u53D1\u5E03\u5F02\u5E38\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
21915
21915
|
}
|
|
21916
21916
|
task.logger.error(errmsg);
|
|
21917
21917
|
return Promise.reject(new Error(errmsg));
|
|
@@ -22059,10 +22059,10 @@ const mockAction$3 = async (task, params) => {
|
|
|
22059
22059
|
|
|
22060
22060
|
const toutiaoPublish = async (task, params) => {
|
|
22061
22061
|
task.logger.info("\u5F00\u59CB\u5934\u6761\u53D1\u5E03");
|
|
22062
|
-
return mockAction$
|
|
22062
|
+
return mockAction$4(task, params);
|
|
22063
22063
|
};
|
|
22064
22064
|
|
|
22065
|
-
const mockAction$
|
|
22065
|
+
const mockAction$3 = async (task, params) => {
|
|
22066
22066
|
const tmpCachePath = task.getTmpPath();
|
|
22067
22067
|
const api = axios$1.create({
|
|
22068
22068
|
headers: {
|
|
@@ -22193,9 +22193,9 @@ const rpaAction$2 = async (task, params) => {
|
|
|
22193
22193
|
await page.locator(".footer-wrap button.publish-content").click();
|
|
22194
22194
|
};
|
|
22195
22195
|
|
|
22196
|
-
const executeAction$
|
|
22196
|
+
const executeAction$2 = (task, params) => {
|
|
22197
22197
|
return new Promise((resolve, reject) => {
|
|
22198
|
-
mockAction$
|
|
22198
|
+
mockAction$3(task, params).then(resolve).catch(() => {
|
|
22199
22199
|
rpaAction$2(task, params).then(resolve).catch(reject);
|
|
22200
22200
|
});
|
|
22201
22201
|
});
|
|
@@ -22206,13 +22206,13 @@ const weitoutiaoPublish = async (task, params) => {
|
|
|
22206
22206
|
return rpaAction$2(task, params);
|
|
22207
22207
|
}
|
|
22208
22208
|
if (params.actionType === "mockApi") {
|
|
22209
|
-
return mockAction$
|
|
22209
|
+
return mockAction$3(task, params);
|
|
22210
22210
|
}
|
|
22211
|
-
return executeAction$
|
|
22211
|
+
return executeAction$2(task, params);
|
|
22212
22212
|
};
|
|
22213
22213
|
|
|
22214
22214
|
const scanRetryMaxCount = 30;
|
|
22215
|
-
const errnoMap = {
|
|
22215
|
+
const errnoMap$1 = {
|
|
22216
22216
|
200003: "\u767B\u5F55\u5931\u6548"
|
|
22217
22217
|
};
|
|
22218
22218
|
const saveDraft$1 = (api, params, data) => {
|
|
@@ -22310,7 +22310,7 @@ const massSend = async (api, params, data) => {
|
|
|
22310
22310
|
});
|
|
22311
22311
|
return res;
|
|
22312
22312
|
};
|
|
22313
|
-
const mockAction$
|
|
22313
|
+
const mockAction$2 = async (task, params) => {
|
|
22314
22314
|
const tmpCachePath = task.getTmpPath();
|
|
22315
22315
|
const api = axios$1.create({
|
|
22316
22316
|
headers: {
|
|
@@ -22322,7 +22322,7 @@ const mockAction$1 = async (task, params) => {
|
|
|
22322
22322
|
(response) => {
|
|
22323
22323
|
const responseData = response.data;
|
|
22324
22324
|
if (responseData?.base_resp && responseData.base_resp.ret !== 0) {
|
|
22325
|
-
const errmsg = errnoMap[responseData.base_resp.ret] || response.config.defaultErrorMsg || responseData.base_resp.err_msg || "Unknown error";
|
|
22325
|
+
const errmsg = errnoMap$1[responseData.base_resp.ret] || response.config.defaultErrorMsg || responseData.base_resp.err_msg || "Unknown error";
|
|
22326
22326
|
task.logger.error(errmsg, responseData);
|
|
22327
22327
|
return Promise.reject(new Error(errmsg));
|
|
22328
22328
|
}
|
|
@@ -22531,7 +22531,7 @@ const mockAction$1 = async (task, params) => {
|
|
|
22531
22531
|
|
|
22532
22532
|
const weixinPublish = async (task, params) => {
|
|
22533
22533
|
task.logger.info("\u5F00\u59CB\u5FAE\u4FE1\u516C\u4F17\u53F7\u53D1\u5E03");
|
|
22534
|
-
return mockAction$
|
|
22534
|
+
return mockAction$2(task, params);
|
|
22535
22535
|
};
|
|
22536
22536
|
|
|
22537
22537
|
const saveDraft = (api, params, data) => {
|
|
@@ -22552,7 +22552,7 @@ const saveDraft = (api, params, data) => {
|
|
|
22552
22552
|
data: formData
|
|
22553
22553
|
});
|
|
22554
22554
|
};
|
|
22555
|
-
const mockAction = async (task, params) => {
|
|
22555
|
+
const mockAction$1 = async (task, params) => {
|
|
22556
22556
|
const tmpCachePath = task.getTmpPath();
|
|
22557
22557
|
const api = axios$1.create({
|
|
22558
22558
|
headers: {
|
|
@@ -22733,9 +22733,9 @@ const rpaAction$1 = async (task, params) => {
|
|
|
22733
22733
|
}
|
|
22734
22734
|
};
|
|
22735
22735
|
|
|
22736
|
-
const executeAction = (task, params) => {
|
|
22736
|
+
const executeAction$1 = (task, params) => {
|
|
22737
22737
|
return new Promise((resolve, reject) => {
|
|
22738
|
-
mockAction(task, params).then(resolve).catch(() => {
|
|
22738
|
+
mockAction$1(task, params).then(resolve).catch(() => {
|
|
22739
22739
|
rpaAction$1(task, params).then(resolve).catch(reject);
|
|
22740
22740
|
});
|
|
22741
22741
|
});
|
|
@@ -22746,16 +22746,252 @@ const weixinmpPublish = async (task, params) => {
|
|
|
22746
22746
|
return rpaAction$1(task, params);
|
|
22747
22747
|
}
|
|
22748
22748
|
if (params.actionType === "mockApi") {
|
|
22749
|
-
return mockAction(task, params);
|
|
22749
|
+
return mockAction$1(task, params);
|
|
22750
22750
|
}
|
|
22751
|
-
return executeAction(task, params);
|
|
22751
|
+
return executeAction$1(task, params);
|
|
22752
22752
|
};
|
|
22753
22753
|
|
|
22754
|
-
var
|
|
22754
|
+
var __defProp = Object.defineProperty;
|
|
22755
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
22756
|
+
var __publicField = (obj, key, value) => {
|
|
22757
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22758
|
+
return value;
|
|
22759
|
+
};
|
|
22760
|
+
class XsEncrypt {
|
|
22761
|
+
constructor() {
|
|
22762
|
+
__publicField(this, "words", [929260340, 1633971297, 895580464, 925905270]);
|
|
22763
|
+
__publicField(this, "keyBytes", Buffer$1.concat(
|
|
22764
|
+
this.words.map(
|
|
22765
|
+
(word) => new Uint8Array(Buffer$1.from([word >> 24 & 255, word >> 16 & 255, word >> 8 & 255, word & 255]))
|
|
22766
|
+
)
|
|
22767
|
+
));
|
|
22768
|
+
__publicField(this, "iv", Buffer$1.from("4uzjr7mbsibcaldp", "utf8"));
|
|
22769
|
+
}
|
|
22770
|
+
/**
|
|
22771
|
+
* 根据传入的url生成MD5摘要
|
|
22772
|
+
*
|
|
22773
|
+
* @param {string} url - API的url
|
|
22774
|
+
* @returns {string} MD5摘要
|
|
22775
|
+
*/
|
|
22776
|
+
async encryptMD5(url) {
|
|
22777
|
+
return crypto.createHash("md5").update(url, "utf8").digest("hex");
|
|
22778
|
+
}
|
|
22779
|
+
/**
|
|
22780
|
+
* 根据传入的text生成AES加密后的内容,并将其转为base64编码
|
|
22781
|
+
*
|
|
22782
|
+
* @param {string} text - 需要加密的字符串
|
|
22783
|
+
* @returns {string} 加密后的base64编码字符串
|
|
22784
|
+
*/
|
|
22785
|
+
async encryptText(text) {
|
|
22786
|
+
const textEncoded = Buffer$1.from(text).toString("base64");
|
|
22787
|
+
const cipher = crypto.createCipheriv("aes-128-cbc", new Uint8Array(this.keyBytes), new Uint8Array(this.iv));
|
|
22788
|
+
const ciphertext = cipher.update(textEncoded, "utf8", "base64");
|
|
22789
|
+
return ciphertext + cipher.final("base64");
|
|
22790
|
+
}
|
|
22791
|
+
/**
|
|
22792
|
+
* 把加密后的payload转为16进制
|
|
22793
|
+
*
|
|
22794
|
+
* @param {string} encodedData - 加密后的payload(Base64编码)
|
|
22795
|
+
* @returns {string} 十六进制字符串
|
|
22796
|
+
*/
|
|
22797
|
+
async base64ToHex(encodedData) {
|
|
22798
|
+
const decodedData = Buffer$1.from(encodedData, "base64");
|
|
22799
|
+
return decodedData.toString("hex");
|
|
22800
|
+
}
|
|
22801
|
+
/**
|
|
22802
|
+
* 把小红书加密参数payload转16进制 再使用base64编码
|
|
22803
|
+
*
|
|
22804
|
+
* @param {string} payload - 要加密处理的payload内容
|
|
22805
|
+
* @param {string} platform - 登录平台
|
|
22806
|
+
* @returns {string} 加密后并进行base64编码的字符串
|
|
22807
|
+
*/
|
|
22808
|
+
async encryptPayload(payload, platform) {
|
|
22809
|
+
const hexPayload = await this.base64ToHex(payload);
|
|
22810
|
+
const obj = {
|
|
22811
|
+
signSvn: "56",
|
|
22812
|
+
signType: "x2",
|
|
22813
|
+
appID: platform,
|
|
22814
|
+
signVersion: "1",
|
|
22815
|
+
payload: hexPayload
|
|
22816
|
+
};
|
|
22817
|
+
const jsonString = JSON.stringify(obj, null, 0);
|
|
22818
|
+
return Buffer$1.from(jsonString).toString("base64");
|
|
22819
|
+
}
|
|
22820
|
+
/**
|
|
22821
|
+
* 将传入的参数加密为小红书的xs
|
|
22822
|
+
* Args: url: API请求的URL
|
|
22823
|
+
* a1: 签名参数a1
|
|
22824
|
+
* ts: 时间戳
|
|
22825
|
+
* platform: 登录平台 默认为xhs-pc-web
|
|
22826
|
+
* Returns:
|
|
22827
|
+
* 最终的加密签名字符串,前缀为“XYW_”
|
|
22828
|
+
*/
|
|
22829
|
+
async encryptXs(url, a1, ts, platform = "xhs-pc-web") {
|
|
22830
|
+
const text = `x1=${await this.encryptMD5(`url=${url}`)};x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0|1|0|0|0;x3=${a1};x4=${ts};`;
|
|
22831
|
+
return `XYW_${await this.encryptPayload(await this.encryptText(text), platform)}`;
|
|
22832
|
+
}
|
|
22833
|
+
dumps(...rest) {
|
|
22834
|
+
const [data, replacer = null, space = 0] = rest;
|
|
22835
|
+
return JSON.stringify(data, replacer, space).replace(/\n/g, "").replace(/":\s+"/g, '":"');
|
|
22836
|
+
}
|
|
22837
|
+
}
|
|
22755
22838
|
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22839
|
+
const errnoMap = {};
|
|
22840
|
+
const xsEncrypt = new XsEncrypt();
|
|
22841
|
+
const mockAction = async (task, params) => {
|
|
22842
|
+
const tmpCachePath = task.getTmpPath();
|
|
22843
|
+
const a1Cookie = params.cookies.find((it) => it.name === "a1")?.value;
|
|
22844
|
+
if (!a1Cookie) {
|
|
22845
|
+
throw new Error("\u672A\u83B7\u53D6\u5230 a1 cookie");
|
|
22846
|
+
}
|
|
22847
|
+
const api = axios$1.create({
|
|
22848
|
+
headers: {
|
|
22849
|
+
cookie: params.cookies.map((it) => `${it.name}=${it.value}`).join(";"),
|
|
22850
|
+
origin: "https://creator.xiaohongshu.com",
|
|
22851
|
+
referer: "https://creator.xiaohongshu.com/",
|
|
22852
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
|
22853
|
+
}
|
|
22854
|
+
});
|
|
22855
|
+
api.interceptors.response.use(
|
|
22856
|
+
(response) => {
|
|
22857
|
+
const responseData = response.data;
|
|
22858
|
+
if (responseData?.code && responseData.code !== 0) {
|
|
22859
|
+
const errmsg = errnoMap[responseData.code] || response.config.defaultErrorMsg || "Unknown error";
|
|
22860
|
+
task.logger.error(errmsg, responseData);
|
|
22861
|
+
return Promise.reject(new Error(errmsg));
|
|
22862
|
+
}
|
|
22863
|
+
return response;
|
|
22864
|
+
},
|
|
22865
|
+
(error) => {
|
|
22866
|
+
task.logger.error("\u5C0F\u7EA2\u4E66\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25", error);
|
|
22867
|
+
return Promise.reject(error);
|
|
22868
|
+
}
|
|
22869
|
+
);
|
|
22870
|
+
const fetchCoverUrl = `/api/media/v1/upload/creator/permit?biz_name=spectrum&scene=image&file_count=${params.banners.length}&version=1&source=web`;
|
|
22871
|
+
const xt = Date.now().toString();
|
|
22872
|
+
const xs = await xsEncrypt.encryptXs(fetchCoverUrl, a1Cookie, xt);
|
|
22873
|
+
const coverIdInfo = await api({
|
|
22874
|
+
method: "get",
|
|
22875
|
+
baseURL: "https://creator.xiaohongshu.com",
|
|
22876
|
+
url: fetchCoverUrl,
|
|
22877
|
+
defaultErrorMsg: "\u83B7\u53D6\u4E0A\u4F20\u9644\u4EF6 token \u5931\u8D25",
|
|
22878
|
+
headers: { "x-s": xs, "x-t": xt }
|
|
22879
|
+
});
|
|
22880
|
+
const coverIds = coverIdInfo.data.data.uploadTempPermits[0].fileIds;
|
|
22881
|
+
const ossToken = coverIdInfo.data.data.uploadTempPermits[0].token;
|
|
22882
|
+
const uploadFile = async (url, index) => {
|
|
22883
|
+
const fileName = getFilenameFromUrl(url);
|
|
22884
|
+
const localUrl = await downloadImage(url, path__default.join(tmpCachePath, fileName));
|
|
22885
|
+
const ossFileId = coverIds[index];
|
|
22886
|
+
const fileBuffer = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c____default.readFileSync(localUrl);
|
|
22887
|
+
await api.put(`https://ros-upload.xiaohongshu.com/${ossFileId}`, fileBuffer, {
|
|
22888
|
+
headers: {
|
|
22889
|
+
"x-cos-security-token": ossToken
|
|
22890
|
+
},
|
|
22891
|
+
defaultErrorMsg: "\u56FE\u7247\u4E0A\u4F20\u51FA\u73B0\u95EE\u9898\uFF0C\u8BF7\u8054\u7CFB\u5BA2\u670D"
|
|
22892
|
+
});
|
|
22893
|
+
return { ossFileId, ossToken };
|
|
22894
|
+
};
|
|
22895
|
+
const coverInfos = await Promise.all(params.banners.map((it, idx) => uploadFile(it, idx)));
|
|
22896
|
+
const publishData = {
|
|
22897
|
+
common: {
|
|
22898
|
+
ats: [],
|
|
22899
|
+
biz_relations: [],
|
|
22900
|
+
desc: params?.content,
|
|
22901
|
+
goods_info: {},
|
|
22902
|
+
hash_tag: [],
|
|
22903
|
+
note_id: "",
|
|
22904
|
+
source: JSON.stringify({
|
|
22905
|
+
type: "web",
|
|
22906
|
+
ids: "",
|
|
22907
|
+
extraInfo: '{"systemId":"web"}'
|
|
22908
|
+
}),
|
|
22909
|
+
title: params?.title,
|
|
22910
|
+
type: "normal",
|
|
22911
|
+
privacy_info: {
|
|
22912
|
+
op_type: 1,
|
|
22913
|
+
type: params.visibleRange === "public" ? 0 : 1
|
|
22914
|
+
},
|
|
22915
|
+
post_loc: params.address ? {
|
|
22916
|
+
name: params.address?.name,
|
|
22917
|
+
poi_id: params.address?.poi_id,
|
|
22918
|
+
poi_type: params.address?.poi_type,
|
|
22919
|
+
subname: params.address?.full_address
|
|
22920
|
+
} : null,
|
|
22921
|
+
business_binds: ""
|
|
22922
|
+
},
|
|
22923
|
+
image_info: {
|
|
22924
|
+
images: coverInfos.map((it) => ({
|
|
22925
|
+
extra_info_json: '{"mimeType":"image/png"}',
|
|
22926
|
+
file_id: it.ossFileId,
|
|
22927
|
+
metadata: { source: -1 },
|
|
22928
|
+
stickers: { floating: [], version: 2 }
|
|
22929
|
+
}))
|
|
22930
|
+
},
|
|
22931
|
+
video_info: null
|
|
22932
|
+
};
|
|
22933
|
+
const userDeclarationBind = {
|
|
22934
|
+
origin: 2,
|
|
22935
|
+
photoInfo: {},
|
|
22936
|
+
repostInfo: {}
|
|
22937
|
+
};
|
|
22938
|
+
if (params.selfDeclaration?.type === "fictional-rendition") {
|
|
22939
|
+
userDeclarationBind.origin = 1;
|
|
22940
|
+
} else if (params.selfDeclaration?.type === "ai-generated") {
|
|
22941
|
+
userDeclarationBind.origin = 2;
|
|
22942
|
+
} else if (params.selfDeclaration?.type === "source-statement") {
|
|
22943
|
+
if (params.selfDeclaration.childType === "self-labeling") {
|
|
22944
|
+
userDeclarationBind.origin = 3;
|
|
22945
|
+
} else if (params.selfDeclaration.childType === "self-shooting") {
|
|
22946
|
+
userDeclarationBind.origin = 4;
|
|
22947
|
+
const photoInfo = {};
|
|
22948
|
+
if (params.selfDeclaration.shootingLocation) {
|
|
22949
|
+
photoInfo.photoPlace = {
|
|
22950
|
+
name: params.selfDeclaration.shootingLocation.name,
|
|
22951
|
+
poiId: params.selfDeclaration.shootingLocation.poi_id,
|
|
22952
|
+
poiType: params.selfDeclaration.shootingLocation.poi_type,
|
|
22953
|
+
subname: params.selfDeclaration.shootingLocation.full_address
|
|
22954
|
+
};
|
|
22955
|
+
}
|
|
22956
|
+
if (params.selfDeclaration.shootingDate) {
|
|
22957
|
+
photoInfo.photoTime = params.selfDeclaration.shootingDate;
|
|
22958
|
+
}
|
|
22959
|
+
userDeclarationBind.photoInfo = photoInfo;
|
|
22960
|
+
} else if (params.selfDeclaration.childType === "transshipment") {
|
|
22961
|
+
userDeclarationBind.origin = 5;
|
|
22962
|
+
if (params.selfDeclaration.sourceMedia) {
|
|
22963
|
+
userDeclarationBind.repostInfo = {
|
|
22964
|
+
source: params.selfDeclaration.sourceMedia
|
|
22965
|
+
};
|
|
22966
|
+
}
|
|
22967
|
+
}
|
|
22968
|
+
}
|
|
22969
|
+
const business_binds = {
|
|
22970
|
+
version: 1,
|
|
22971
|
+
bizType: "",
|
|
22972
|
+
noteId: "",
|
|
22973
|
+
noteOrderBind: {},
|
|
22974
|
+
// 发布时间 - 立即发布/定时发布
|
|
22975
|
+
notePostTiming: params.isImmediatelyPublish ? {} : { postTime: params.scheduledPublish },
|
|
22976
|
+
noteCollectionBind: { id: "" },
|
|
22977
|
+
...params.selfDeclaration ? { userDeclarationBind } : {}
|
|
22978
|
+
};
|
|
22979
|
+
publishData.common.business_binds = JSON.stringify(business_binds);
|
|
22980
|
+
const publishDataStr = xsEncrypt.dumps(publishData);
|
|
22981
|
+
const publishXt = Date.now().toString();
|
|
22982
|
+
const publishXs = await xsEncrypt.encryptXs(`/web_api/sns/v2/note${publishDataStr}`, a1Cookie, publishXt);
|
|
22983
|
+
const publishResult = await api({
|
|
22984
|
+
method: "post",
|
|
22985
|
+
url: "https://edith.xiaohongshu.com/web_api/sns/v2/note",
|
|
22986
|
+
data: publishData,
|
|
22987
|
+
headers: {
|
|
22988
|
+
"x-s": publishXs,
|
|
22989
|
+
"x-t": publishXt
|
|
22990
|
+
},
|
|
22991
|
+
defaultErrorMsg: "\u6587\u7AE0\u53D1\u5E03\u51FA\u73B0\u95EE\u9898\uFF0C\u8BF7\u8054\u7CFB\u5BA2\u670D"
|
|
22992
|
+
});
|
|
22993
|
+
return publishResult.data.data.id;
|
|
22994
|
+
};
|
|
22759
22995
|
|
|
22760
22996
|
const rpaAction = async (task, params) => {
|
|
22761
22997
|
const commonCookies = {
|
|
@@ -22904,9 +23140,22 @@ const rpaAction = async (task, params) => {
|
|
|
22904
23140
|
return response;
|
|
22905
23141
|
};
|
|
22906
23142
|
|
|
23143
|
+
const executeAction = (task, params) => {
|
|
23144
|
+
return new Promise((resolve, reject) => {
|
|
23145
|
+
mockAction(task, params).then(resolve).catch(() => {
|
|
23146
|
+
rpaAction(task, params).then(resolve).catch(reject);
|
|
23147
|
+
});
|
|
23148
|
+
});
|
|
23149
|
+
};
|
|
22907
23150
|
const xiaohongshuPublish = async (task, params) => {
|
|
22908
23151
|
task.logger.info("\u5F00\u59CB\u5C0F\u7EA2\u4E66\u53D1\u5E03");
|
|
22909
|
-
|
|
23152
|
+
if (params.actionType === "rpa") {
|
|
23153
|
+
return rpaAction(task, params);
|
|
23154
|
+
}
|
|
23155
|
+
if (params.actionType === "mockApi") {
|
|
23156
|
+
return mockAction(task, params);
|
|
23157
|
+
}
|
|
23158
|
+
return executeAction(task, params);
|
|
22910
23159
|
};
|
|
22911
23160
|
|
|
22912
23161
|
class Action {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iflyrpa/actions",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/mime-types": "^2.1.4",
|
|
16
16
|
"axios": "^1.7.8",
|
|
17
|
-
"dayjs": "^1.11.13",
|
|
18
17
|
"dom-serializer": "^2.0.0",
|
|
19
18
|
"domhandler": "^5.0.3",
|
|
20
19
|
"form-data": "^4.0.1",
|
|
@@ -49,15 +49,13 @@ const executeAction: PublishAction = (task, params) => {
|
|
|
49
49
|
export const xiaohongshuPublish: PublishAction = async (task, params) => {
|
|
50
50
|
task.logger.info("开始小红书发布");
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
if (params.actionType === "rpa") {
|
|
53
|
+
return rpaAction(task, params);
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
if (params.actionType === "mockApi") {
|
|
57
|
+
return mockAction(task, params);
|
|
58
|
+
}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
// return mockAction(task, params);
|
|
60
|
-
// }
|
|
61
|
-
|
|
62
|
-
// return executeAction(task, params);
|
|
60
|
+
return executeAction(task, params);
|
|
63
61
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { downloadImage, getFilenameFromUrl, sleep } from "@iflyrpa/share";
|
|
3
|
+
import { downloadImage, getFilenameFromUrl } from "@iflyrpa/share";
|
|
5
4
|
import axios, { type AxiosResponse } from "axios";
|
|
6
|
-
import dayjs from "dayjs";
|
|
7
5
|
import type { PublishAction } from ".";
|
|
6
|
+
import { XsEncrypt } from "../../utils/xhsXsEncrypt";
|
|
8
7
|
|
|
9
8
|
interface CommonResponse<T = unknown> {
|
|
10
9
|
data: T;
|
|
@@ -20,35 +19,24 @@ interface UploadTempPermit {
|
|
|
20
19
|
|
|
21
20
|
const errnoMap: Record<number, string> = {};
|
|
22
21
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
// sameSite: "lax",
|
|
26
|
-
// secure: false,
|
|
27
|
-
// domain: "xiaohongshu.com",
|
|
28
|
-
// url: "https://creator.xiaohongshu.com",
|
|
29
|
-
// httpOnly: true,
|
|
30
|
-
// };
|
|
22
|
+
// 小红书参数加密器
|
|
23
|
+
const xsEncrypt = new XsEncrypt();
|
|
31
24
|
|
|
32
25
|
export const mockAction: PublishAction = async (task, params) => {
|
|
33
|
-
// const page = await task.createPage({
|
|
34
|
-
// show: task.debug,
|
|
35
|
-
// url: params.url || "https://creator.xiaohongshu.com/publish/publish",
|
|
36
|
-
// cookies: params.cookies?.map((it) => ({ ...commonCookies, ...it })) || [],
|
|
37
|
-
// });
|
|
38
26
|
const tmpCachePath = task.getTmpPath();
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// const xsValue = await page.evaluate(([arg]) => window._webmsxyw(), [fetchCoverArg]);
|
|
28
|
+
const a1Cookie = params.cookies.find((it) => it.name === "a1")?.value;
|
|
29
|
+
if (!a1Cookie) {
|
|
30
|
+
throw new Error("未获取到 a1 cookie");
|
|
31
|
+
}
|
|
45
32
|
|
|
46
33
|
const api = axios.create({
|
|
47
34
|
headers: {
|
|
48
35
|
cookie: params.cookies.map((it) => `${it.name}=${it.value}`).join(";"),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
origin: "https://creator.xiaohongshu.com",
|
|
37
|
+
referer: "https://creator.xiaohongshu.com/",
|
|
38
|
+
"user-agent":
|
|
39
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
|
52
40
|
},
|
|
53
41
|
});
|
|
54
42
|
|
|
@@ -69,17 +57,22 @@ export const mockAction: PublishAction = async (task, params) => {
|
|
|
69
57
|
return response;
|
|
70
58
|
},
|
|
71
59
|
(error) => {
|
|
72
|
-
task.logger.error("
|
|
60
|
+
task.logger.error("小红书接口请求失败", error);
|
|
73
61
|
// 处理响应错误
|
|
74
62
|
return Promise.reject(error);
|
|
75
63
|
},
|
|
76
64
|
);
|
|
77
65
|
|
|
78
66
|
// 0. 获取上传 token
|
|
67
|
+
const fetchCoverUrl = `/api/media/v1/upload/creator/permit?biz_name=spectrum&scene=image&file_count=${params.banners.length}&version=1&source=web`;
|
|
68
|
+
const xt = Date.now().toString();
|
|
69
|
+
const xs = await xsEncrypt.encryptXs(fetchCoverUrl, a1Cookie, xt);
|
|
79
70
|
const coverIdInfo = await api<{ data: { uploadTempPermits: UploadTempPermit[] } }>({
|
|
80
71
|
method: "get",
|
|
81
|
-
|
|
72
|
+
baseURL: "https://creator.xiaohongshu.com",
|
|
73
|
+
url: fetchCoverUrl,
|
|
82
74
|
defaultErrorMsg: "获取上传附件 token 失败",
|
|
75
|
+
headers: { "x-s": xs, "x-t": xt },
|
|
83
76
|
});
|
|
84
77
|
|
|
85
78
|
const coverIds = coverIdInfo.data.data.uploadTempPermits[0].fileIds;
|
|
@@ -98,7 +91,7 @@ export const mockAction: PublishAction = async (task, params) => {
|
|
|
98
91
|
headers: {
|
|
99
92
|
"x-cos-security-token": ossToken,
|
|
100
93
|
},
|
|
101
|
-
defaultErrorMsg: "
|
|
94
|
+
defaultErrorMsg: "图片上传出现问题,请联系客服",
|
|
102
95
|
});
|
|
103
96
|
|
|
104
97
|
return { ossFileId, ossToken };
|
|
@@ -139,13 +132,8 @@ export const mockAction: PublishAction = async (task, params) => {
|
|
|
139
132
|
images: coverInfos.map((it) => ({
|
|
140
133
|
extra_info_json: '{"mimeType":"image/png"}',
|
|
141
134
|
file_id: it.ossFileId,
|
|
142
|
-
metadata: {
|
|
143
|
-
|
|
144
|
-
},
|
|
145
|
-
stickers: {
|
|
146
|
-
floating: [],
|
|
147
|
-
version: 2,
|
|
148
|
-
},
|
|
135
|
+
metadata: { source: -1 },
|
|
136
|
+
stickers: { floating: [], version: 2 },
|
|
149
137
|
})),
|
|
150
138
|
},
|
|
151
139
|
video_info: null,
|
|
@@ -208,15 +196,29 @@ export const mockAction: PublishAction = async (task, params) => {
|
|
|
208
196
|
bizType: "",
|
|
209
197
|
noteId: "",
|
|
210
198
|
noteOrderBind: {},
|
|
211
|
-
// 发布时间 -
|
|
212
|
-
notePostTiming: params.isImmediatelyPublish ? {
|
|
199
|
+
// 发布时间 - 立即发布/定时发布
|
|
200
|
+
notePostTiming: params.isImmediatelyPublish ? {} : { postTime: params.scheduledPublish },
|
|
213
201
|
noteCollectionBind: { id: "" },
|
|
214
202
|
...(params.selfDeclaration ? { userDeclarationBind } : {}),
|
|
215
203
|
};
|
|
216
204
|
|
|
217
205
|
publishData.common.business_binds = JSON.stringify(business_binds);
|
|
218
206
|
|
|
219
|
-
|
|
207
|
+
const publishDataStr = xsEncrypt.dumps(publishData);
|
|
208
|
+
const publishXt = Date.now().toString();
|
|
209
|
+
const publishXs = await xsEncrypt.encryptXs(`/web_api/sns/v2/note${publishDataStr}`, a1Cookie, publishXt);
|
|
210
|
+
|
|
211
|
+
// 发布
|
|
212
|
+
const publishResult = await api<CommonResponse<{ id: string }>>({
|
|
213
|
+
method: "post",
|
|
214
|
+
url: "https://edith.xiaohongshu.com/web_api/sns/v2/note",
|
|
215
|
+
data: publishData,
|
|
216
|
+
headers: {
|
|
217
|
+
"x-s": publishXs,
|
|
218
|
+
"x-t": publishXt,
|
|
219
|
+
},
|
|
220
|
+
defaultErrorMsg: "文章发布出现问题,请联系客服",
|
|
221
|
+
});
|
|
220
222
|
|
|
221
|
-
return
|
|
223
|
+
return publishResult.data.data.id;
|
|
222
224
|
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 小红书加密参数逆向,代码来自:https://github.com/Cloxl/xhshow
|
|
6
|
+
*/
|
|
7
|
+
export class XsEncrypt {
|
|
8
|
+
private words = [929260340, 1633971297, 895580464, 925905270];
|
|
9
|
+
private keyBytes = Buffer.concat(
|
|
10
|
+
this.words.map(
|
|
11
|
+
(word) =>
|
|
12
|
+
new Uint8Array(Buffer.from([(word >> 24) & 0xff, (word >> 16) & 0xff, (word >> 8) & 0xff, word & 0xff])),
|
|
13
|
+
),
|
|
14
|
+
);
|
|
15
|
+
private iv = Buffer.from("4uzjr7mbsibcaldp", "utf8");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 根据传入的url生成MD5摘要
|
|
19
|
+
*
|
|
20
|
+
* @param {string} url - API的url
|
|
21
|
+
* @returns {string} MD5摘要
|
|
22
|
+
*/
|
|
23
|
+
async encryptMD5(url: string): Promise<string> {
|
|
24
|
+
return crypto.createHash("md5").update(url, "utf8").digest("hex");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 根据传入的text生成AES加密后的内容,并将其转为base64编码
|
|
29
|
+
*
|
|
30
|
+
* @param {string} text - 需要加密的字符串
|
|
31
|
+
* @returns {string} 加密后的base64编码字符串
|
|
32
|
+
*/
|
|
33
|
+
async encryptText(text: string): Promise<string> {
|
|
34
|
+
// 将文本编码为 Base64
|
|
35
|
+
const textEncoded = Buffer.from(text).toString("base64");
|
|
36
|
+
// 创建 AES 加密器
|
|
37
|
+
const cipher = crypto.createCipheriv("aes-128-cbc", new Uint8Array(this.keyBytes), new Uint8Array(this.iv));
|
|
38
|
+
// 对数据进行填充
|
|
39
|
+
const ciphertext = cipher.update(textEncoded, "utf8", "base64");
|
|
40
|
+
return ciphertext + cipher.final("base64");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 把加密后的payload转为16进制
|
|
45
|
+
*
|
|
46
|
+
* @param {string} encodedData - 加密后的payload(Base64编码)
|
|
47
|
+
* @returns {string} 十六进制字符串
|
|
48
|
+
*/
|
|
49
|
+
async base64ToHex(encodedData: string): Promise<string> {
|
|
50
|
+
// 将 Base64 字符串解码为二进制数据
|
|
51
|
+
const decodedData = Buffer.from(encodedData, "base64");
|
|
52
|
+
// 将二进制数据转换为十六进制字符串
|
|
53
|
+
return decodedData.toString("hex");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 把小红书加密参数payload转16进制 再使用base64编码
|
|
58
|
+
*
|
|
59
|
+
* @param {string} payload - 要加密处理的payload内容
|
|
60
|
+
* @param {string} platform - 登录平台
|
|
61
|
+
* @returns {string} 加密后并进行base64编码的字符串
|
|
62
|
+
*/
|
|
63
|
+
async encryptPayload(payload: string, platform: string): Promise<string> {
|
|
64
|
+
// 将 payload 转换为十六进制
|
|
65
|
+
const hexPayload = await this.base64ToHex(payload);
|
|
66
|
+
|
|
67
|
+
// 构造对象
|
|
68
|
+
const obj = {
|
|
69
|
+
signSvn: "56",
|
|
70
|
+
signType: "x2",
|
|
71
|
+
appID: platform,
|
|
72
|
+
signVersion: "1",
|
|
73
|
+
payload: hexPayload,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// 将对象转换为 JSON 字符串(紧凑格式)
|
|
77
|
+
const jsonString = JSON.stringify(obj, null, 0);
|
|
78
|
+
|
|
79
|
+
// 对 JSON 字符串进行 Base64 编码
|
|
80
|
+
return Buffer.from(jsonString).toString("base64");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 将传入的参数加密为小红书的xs
|
|
85
|
+
* Args: url: API请求的URL
|
|
86
|
+
* a1: 签名参数a1
|
|
87
|
+
* ts: 时间戳
|
|
88
|
+
* platform: 登录平台 默认为xhs-pc-web
|
|
89
|
+
* Returns:
|
|
90
|
+
* 最终的加密签名字符串,前缀为“XYW_”
|
|
91
|
+
*/
|
|
92
|
+
async encryptXs(url: string, a1: string, ts: string, platform = "xhs-pc-web"): Promise<string> {
|
|
93
|
+
const text = `x1=${await this.encryptMD5(`url=${url}`)};x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0|1|0|0|0;x3=${a1};x4=${ts};`;
|
|
94
|
+
return `XYW_${await this.encryptPayload(await this.encryptText(text), platform)}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
dumps(...rest: Parameters<typeof JSON.stringify>): string {
|
|
98
|
+
const [data, replacer = null, space = 0] = rest;
|
|
99
|
+
return JSON.stringify(data, replacer, space)
|
|
100
|
+
.replace(/\n/g, "")
|
|
101
|
+
.replace(/":\s+"/g, '":"');
|
|
102
|
+
}
|
|
103
|
+
}
|