@iflyrpa/playwright 1.0.8 → 1.0.10-beta.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/dist/index.cjs +332 -86
- package/dist/index.d.cts +31 -4
- package/dist/index.d.mts +31 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.mjs +328 -81
- package/package.json +3 -12
package/dist/index.cjs
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const spawn = require('cross-spawn');
|
|
5
|
+
const https = require('node:https');
|
|
6
|
+
const log = require('loglevel');
|
|
7
|
+
const node_child_process = require('node:child_process');
|
|
9
8
|
|
|
10
9
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
11
10
|
|
|
12
|
-
const semver__default = /*#__PURE__*/_interopDefaultCompat(semver);
|
|
13
|
-
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
14
|
-
const axios__default = /*#__PURE__*/_interopDefaultCompat(axios);
|
|
15
11
|
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
16
|
-
const
|
|
12
|
+
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
13
|
+
const https__default = /*#__PURE__*/_interopDefaultCompat(https);
|
|
14
|
+
const log__default = /*#__PURE__*/_interopDefaultCompat(log);
|
|
17
15
|
|
|
18
16
|
const name = "@iflyrpa/playwright";
|
|
19
17
|
const type = "module";
|
|
20
|
-
const version$1 = "1.0.
|
|
18
|
+
const version$1 = "1.0.10-beta.0";
|
|
21
19
|
const description = "";
|
|
22
20
|
const main = "./dist/index.cjs";
|
|
23
21
|
const module$1 = "./dist/index.mjs";
|
|
@@ -35,23 +33,14 @@ const files = [
|
|
|
35
33
|
"dist"
|
|
36
34
|
];
|
|
37
35
|
const dependencies = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
deepmerge: "^4.3.1",
|
|
41
|
-
"fs-extra": "^11.2.0",
|
|
42
|
-
playwright: "^1.46.1",
|
|
43
|
-
semver: "^7.6.3"
|
|
36
|
+
loglevel: "^1.9.2",
|
|
37
|
+
playwright: "^1.46.1"
|
|
44
38
|
};
|
|
45
39
|
const peerDependencies = {
|
|
46
40
|
electron: "*"
|
|
47
41
|
};
|
|
48
42
|
const devDependencies = {
|
|
49
|
-
"@types/cross-spawn": "^6.0.6",
|
|
50
|
-
"@types/fs-extra": "^11.0.4",
|
|
51
|
-
"@types/semver": "^7.5.8",
|
|
52
|
-
bumpp: "^9.5.2",
|
|
53
43
|
esno: "^4.7.0",
|
|
54
|
-
"ts-node": "^10.9.2",
|
|
55
44
|
typescript: "^5.5.2",
|
|
56
45
|
unbuild: "^2.0.0"
|
|
57
46
|
};
|
|
@@ -73,32 +62,166 @@ const packageJson = {
|
|
|
73
62
|
devDependencies: devDependencies
|
|
74
63
|
};
|
|
75
64
|
|
|
76
|
-
async function downloadImage(url,
|
|
77
|
-
|
|
78
|
-
url,
|
|
79
|
-
method: "GET",
|
|
80
|
-
responseType: "stream"
|
|
81
|
-
// 重要:设置响应类型为 'stream'
|
|
82
|
-
});
|
|
83
|
-
await fs__default.ensureFile(path2);
|
|
84
|
-
const writer = fs__default.createWriteStream(path2);
|
|
85
|
-
response.data.pipe(writer);
|
|
65
|
+
async function downloadImage(url, savePath) {
|
|
66
|
+
await ensureFile(savePath);
|
|
86
67
|
return new Promise((resolve, reject) => {
|
|
87
|
-
|
|
88
|
-
|
|
68
|
+
https__default.get(url, (response) => {
|
|
69
|
+
if (response.statusCode === 200) {
|
|
70
|
+
const fileStream = fs__default.createWriteStream(savePath);
|
|
71
|
+
response.pipe(fileStream);
|
|
72
|
+
fileStream.on("finish", () => {
|
|
73
|
+
fileStream.close();
|
|
74
|
+
console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
|
|
75
|
+
resolve(savePath);
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
|
|
79
|
+
response.resume();
|
|
80
|
+
reject();
|
|
81
|
+
}
|
|
82
|
+
}).on("error", (error) => {
|
|
83
|
+
console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
|
|
84
|
+
reject();
|
|
85
|
+
});
|
|
89
86
|
});
|
|
90
87
|
}
|
|
91
88
|
function getFilenameFromUrl(imageUrl) {
|
|
92
89
|
const parsedUrl = new URL(imageUrl);
|
|
93
90
|
return path__default.basename(parsedUrl.pathname);
|
|
94
91
|
}
|
|
92
|
+
function pathExists(path2) {
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
fs__default.stat(path2, (err) => {
|
|
95
|
+
resolve(!err);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function ensureFile(filePath) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const dirPath = path__default.dirname(filePath);
|
|
102
|
+
fs__default.stat(dirPath, (err, stats) => {
|
|
103
|
+
if (err) {
|
|
104
|
+
if (err.code === "ENOENT") {
|
|
105
|
+
fs__default.mkdir(dirPath, { recursive: true }, (err2) => {
|
|
106
|
+
if (err2) {
|
|
107
|
+
return reject(err2);
|
|
108
|
+
}
|
|
109
|
+
createFile();
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
return reject(err);
|
|
113
|
+
}
|
|
114
|
+
} else if (stats.isDirectory()) {
|
|
115
|
+
checkFile();
|
|
116
|
+
} else {
|
|
117
|
+
reject(new Error(`${dirPath} is not a directory`));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
function checkFile() {
|
|
121
|
+
fs__default.stat(filePath, (err, stats) => {
|
|
122
|
+
if (err) {
|
|
123
|
+
if (err.code === "ENOENT") {
|
|
124
|
+
createFile();
|
|
125
|
+
} else {
|
|
126
|
+
reject(err);
|
|
127
|
+
}
|
|
128
|
+
} else if (stats.isFile()) {
|
|
129
|
+
resolve();
|
|
130
|
+
} else {
|
|
131
|
+
reject(new Error(`${filePath} is not a file`));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function createFile() {
|
|
136
|
+
fs__default.writeFile(filePath, "", (err) => {
|
|
137
|
+
if (err) {
|
|
138
|
+
reject(err);
|
|
139
|
+
} else {
|
|
140
|
+
resolve();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function ensureFileSync(filePath) {
|
|
147
|
+
const dirPath = path__default.dirname(filePath);
|
|
148
|
+
try {
|
|
149
|
+
if (!fs__default.existsSync(dirPath)) {
|
|
150
|
+
fs__default.mkdirSync(dirPath, { recursive: true });
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err instanceof Error) {
|
|
154
|
+
throw new Error(`Error creating directory: ${err.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
if (!fs__default.existsSync(filePath)) {
|
|
159
|
+
fs__default.writeFileSync(filePath, "");
|
|
160
|
+
}
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err instanceof Error) {
|
|
163
|
+
throw new Error(`Error creating file: ${err.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function writeFile(filePath, data) {
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
fs__default.writeFile(filePath, data, (err) => {
|
|
170
|
+
if (err) {
|
|
171
|
+
console.error("Error writing file:", err);
|
|
172
|
+
reject();
|
|
173
|
+
} else {
|
|
174
|
+
console.log("File written successfully");
|
|
175
|
+
resolve();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function compareVersions(v1, v2) {
|
|
181
|
+
const parts1 = v1.split(".").map(Number);
|
|
182
|
+
const parts2 = v2.split(".").map(Number);
|
|
183
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
184
|
+
const num1 = i < parts1.length ? parts1[i] : 0;
|
|
185
|
+
const num2 = i < parts2.length ? parts2[i] : 0;
|
|
186
|
+
if (num1 > num2)
|
|
187
|
+
return 1;
|
|
188
|
+
if (num1 < num2)
|
|
189
|
+
return -1;
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
const semver = {
|
|
194
|
+
gt: (v1, v2) => compareVersions(v1, v2) === 1
|
|
195
|
+
};
|
|
196
|
+
function fetchJSON(url) {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
https__default.get(url, (res) => {
|
|
199
|
+
let data = "";
|
|
200
|
+
res.on("data", (chunk) => {
|
|
201
|
+
data += chunk;
|
|
202
|
+
});
|
|
203
|
+
res.on("end", () => {
|
|
204
|
+
try {
|
|
205
|
+
const parsedData = JSON.parse(data);
|
|
206
|
+
resolve(parsedData);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
if (e instanceof Error) {
|
|
209
|
+
reject(new Error(`Error parsing JSON: ${e.message}`));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}).on("error", (err) => {
|
|
214
|
+
reject(new Error(`Request failed: ${err.message}`));
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
95
218
|
|
|
96
219
|
const visibleRangeTexts = {
|
|
97
220
|
public: "\u516C\u5F00",
|
|
98
221
|
private: "\u79C1\u5BC6"
|
|
99
222
|
};
|
|
100
223
|
const xiaohongshuPublishAction = async (props) => {
|
|
101
|
-
const { page,
|
|
224
|
+
const { page, tmpCachePath, params } = props;
|
|
102
225
|
const selectAddress = async (selector, address) => {
|
|
103
226
|
const instance = typeof selector === "string" ? page.locator(selector) : selector;
|
|
104
227
|
await instance.click();
|
|
@@ -121,11 +244,13 @@ const xiaohongshuPublishAction = async (props) => {
|
|
|
121
244
|
await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
|
|
122
245
|
throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
|
|
123
246
|
});
|
|
124
|
-
await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click()
|
|
247
|
+
await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click().catch(() => {
|
|
248
|
+
throw new Error("\u672A\u627E\u5230\u4E0A\u4F20\u56FE\u6587\u6309\u94AE");
|
|
249
|
+
});
|
|
125
250
|
const images = await Promise.all(
|
|
126
251
|
params.banners.map((url) => {
|
|
127
252
|
const fileName = getFilenameFromUrl(url);
|
|
128
|
-
return downloadImage(url, path__default.join(
|
|
253
|
+
return downloadImage(url, path__default.join(tmpCachePath, fileName));
|
|
129
254
|
})
|
|
130
255
|
);
|
|
131
256
|
const fileChooserPromise = page.waitForEvent("filechooser");
|
|
@@ -215,6 +340,64 @@ const xiaohongshuPublishAction = async (props) => {
|
|
|
215
340
|
return response;
|
|
216
341
|
};
|
|
217
342
|
|
|
343
|
+
var __defProp$2 = Object.defineProperty;
|
|
344
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
345
|
+
var __publicField$2 = (obj, key, value) => {
|
|
346
|
+
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
347
|
+
return value;
|
|
348
|
+
};
|
|
349
|
+
const _Logger = class _Logger {
|
|
350
|
+
constructor(cachePath) {
|
|
351
|
+
__publicField$2(this, "stream");
|
|
352
|
+
if (_Logger.instance) {
|
|
353
|
+
return _Logger.instance;
|
|
354
|
+
}
|
|
355
|
+
const logFile = path__default.join(cachePath, "rpa.log");
|
|
356
|
+
this.stream = fs__default.createWriteStream(logFile, { flags: "a" });
|
|
357
|
+
log__default.setLevel("debug");
|
|
358
|
+
log__default.methodFactory = (methodName) => {
|
|
359
|
+
return (message) => {
|
|
360
|
+
this.stream.write(
|
|
361
|
+
`[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
|
|
362
|
+
`
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
log__default.setLevel(log__default.getLevel());
|
|
367
|
+
_Logger.instance = this;
|
|
368
|
+
}
|
|
369
|
+
static getInstance(cachePath) {
|
|
370
|
+
if (!_Logger.instance) {
|
|
371
|
+
_Logger.instance = new _Logger(cachePath);
|
|
372
|
+
}
|
|
373
|
+
return _Logger.instance;
|
|
374
|
+
}
|
|
375
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
376
|
+
debug(...msg) {
|
|
377
|
+
log__default.debug(...msg);
|
|
378
|
+
}
|
|
379
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
380
|
+
info(...msg) {
|
|
381
|
+
log__default.info(...msg);
|
|
382
|
+
}
|
|
383
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
384
|
+
warn(...msg) {
|
|
385
|
+
log__default.warn(...msg);
|
|
386
|
+
}
|
|
387
|
+
error(prefix, error) {
|
|
388
|
+
log__default.error(prefix);
|
|
389
|
+
if (error instanceof Error) {
|
|
390
|
+
log__default.error(`${error.message}
|
|
391
|
+
Error stack: ${error.stack}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
close() {
|
|
395
|
+
this.stream.end();
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
__publicField$2(_Logger, "instance", null);
|
|
399
|
+
let Logger = _Logger;
|
|
400
|
+
|
|
218
401
|
const template = `
|
|
219
402
|
const { app } = require("electron");
|
|
220
403
|
|
|
@@ -224,22 +407,35 @@ app.on("window-all-closed", (e) => e.preventDefault());
|
|
|
224
407
|
`;
|
|
225
408
|
const generateFile = async (dir) => {
|
|
226
409
|
const filePath = path__default.join(dir, "src", "main.js");
|
|
227
|
-
const
|
|
228
|
-
if (!
|
|
229
|
-
await
|
|
230
|
-
await
|
|
410
|
+
const isPathExists = await pathExists(filePath);
|
|
411
|
+
if (!isPathExists) {
|
|
412
|
+
await ensureFile(filePath);
|
|
413
|
+
await writeFile(filePath, template);
|
|
231
414
|
}
|
|
232
415
|
return filePath;
|
|
233
416
|
};
|
|
234
417
|
const createElectronApp = async (cachePath, playwrightPackage) => {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
418
|
+
const logger = Logger.getInstance(cachePath);
|
|
419
|
+
try {
|
|
420
|
+
const executablePath = path__default.join(
|
|
421
|
+
cachePath,
|
|
422
|
+
"node_modules",
|
|
423
|
+
".bin",
|
|
424
|
+
"electron"
|
|
425
|
+
);
|
|
426
|
+
const playwright = await playwrightPackage;
|
|
427
|
+
const mainPath = await generateFile(cachePath);
|
|
428
|
+
const electronApp = await playwright._electron.launch({
|
|
429
|
+
executablePath,
|
|
430
|
+
// 获取 Electron 可执行文件的路径
|
|
431
|
+
args: [mainPath],
|
|
432
|
+
cwd: cachePath
|
|
433
|
+
});
|
|
434
|
+
logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
|
|
435
|
+
return electronApp;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
|
|
438
|
+
}
|
|
243
439
|
};
|
|
244
440
|
|
|
245
441
|
var __defProp$1 = Object.defineProperty;
|
|
@@ -255,8 +451,11 @@ class PackageManager {
|
|
|
255
451
|
__publicField$1(this, "forceUpdate");
|
|
256
452
|
// 是否强制更新
|
|
257
453
|
__publicField$1(this, "initPromise");
|
|
454
|
+
__publicField$1(this, "logger");
|
|
455
|
+
this.logger = Logger.getInstance(params.cacheDir);
|
|
258
456
|
this.cacheDir = params.cacheDir;
|
|
259
457
|
this.forceUpdate = params.forceUpdate || true;
|
|
458
|
+
this.initCacheProject();
|
|
260
459
|
this.initPromise = this.init(params.packageName, params.packageVersion);
|
|
261
460
|
}
|
|
262
461
|
// 在子线程执行 npm 命令
|
|
@@ -264,7 +463,8 @@ class PackageManager {
|
|
|
264
463
|
return new Promise((resolve) => {
|
|
265
464
|
const args = [cmd].concat(modules).concat("--color=always").concat("--save");
|
|
266
465
|
try {
|
|
267
|
-
const
|
|
466
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
467
|
+
const npm = node_child_process.spawn(npmCmd, args, { cwd: where });
|
|
268
468
|
let output = "";
|
|
269
469
|
npm.stdout?.on("data", (data) => {
|
|
270
470
|
output += data;
|
|
@@ -280,11 +480,10 @@ class PackageManager {
|
|
|
280
480
|
}
|
|
281
481
|
});
|
|
282
482
|
npm.on("error", (err) => {
|
|
283
|
-
|
|
284
|
-
console.error(err);
|
|
483
|
+
this.logger.error("npm run error", err);
|
|
285
484
|
});
|
|
286
485
|
} catch (error) {
|
|
287
|
-
|
|
486
|
+
this.logger.error(`npm ${args.join(" ")}: `, error);
|
|
288
487
|
}
|
|
289
488
|
});
|
|
290
489
|
}
|
|
@@ -292,21 +491,23 @@ class PackageManager {
|
|
|
292
491
|
initCacheProject() {
|
|
293
492
|
const packagePath = path__default.join(this.cacheDir, "package.json");
|
|
294
493
|
if (!fs__default.existsSync(packagePath)) {
|
|
494
|
+
this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
|
|
295
495
|
const pkg = {
|
|
296
496
|
name: "rpa-plugins",
|
|
297
497
|
description: "rpa-plugins",
|
|
298
|
-
license: "MIT"
|
|
498
|
+
license: "MIT",
|
|
499
|
+
main: "./src/main.js"
|
|
299
500
|
};
|
|
300
|
-
|
|
501
|
+
ensureFileSync(packagePath);
|
|
301
502
|
fs__default.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
|
|
302
503
|
}
|
|
303
504
|
}
|
|
304
505
|
// 获取依赖信息
|
|
305
506
|
async getPluginInfo(module) {
|
|
306
|
-
const res = await
|
|
507
|
+
const res = await fetchJSON(
|
|
307
508
|
`https://registry.npmjs.com/-/v1/search?text=${module}`
|
|
308
509
|
);
|
|
309
|
-
const packages = res.
|
|
510
|
+
const packages = res.objects;
|
|
310
511
|
return packages.find((it) => it.package.name === module)?.package;
|
|
311
512
|
}
|
|
312
513
|
// 查询本地安装的依赖
|
|
@@ -315,6 +516,7 @@ class PackageManager {
|
|
|
315
516
|
try {
|
|
316
517
|
return require(path__default.join(pluginDir, module));
|
|
317
518
|
} catch (error) {
|
|
519
|
+
this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
|
|
318
520
|
return null;
|
|
319
521
|
}
|
|
320
522
|
}
|
|
@@ -332,20 +534,23 @@ class PackageManager {
|
|
|
332
534
|
return this.execCommand("update", [module]);
|
|
333
535
|
}
|
|
334
536
|
async init(name, version) {
|
|
335
|
-
this.initCacheProject();
|
|
336
537
|
const plugin = this.getPlugin(path__default.join(name, "package.json"));
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
538
|
+
try {
|
|
539
|
+
if (!plugin) {
|
|
540
|
+
await this.install(name, version);
|
|
541
|
+
} else if (this.forceUpdate) {
|
|
542
|
+
const pkInfo = await this.getPluginInfo(name);
|
|
543
|
+
if (!pkInfo)
|
|
544
|
+
return;
|
|
545
|
+
const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
|
|
546
|
+
if (hasNewVersion) {
|
|
547
|
+
await this.install(name, pkInfo.version);
|
|
548
|
+
}
|
|
346
549
|
}
|
|
550
|
+
this.logger.info(`${name} package manager init done!`);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
this.logger.error(`${name} package manager init error`, error);
|
|
347
553
|
}
|
|
348
|
-
console.log("Package manager init done!");
|
|
349
554
|
}
|
|
350
555
|
}
|
|
351
556
|
|
|
@@ -357,18 +562,16 @@ var __publicField = (obj, key, value) => {
|
|
|
357
562
|
};
|
|
358
563
|
const PLAYWRIGHT_VERSION = "1.46.1";
|
|
359
564
|
class LocalAutomateTask {
|
|
360
|
-
constructor(
|
|
565
|
+
constructor({ cachePath, debug }) {
|
|
361
566
|
__publicField(this, "cachePath");
|
|
362
567
|
__publicField(this, "debug");
|
|
363
|
-
__publicField(this, "appWhenReady");
|
|
364
568
|
__publicField(this, "playwrightPackage");
|
|
365
|
-
this
|
|
366
|
-
this
|
|
569
|
+
__publicField(this, "logger");
|
|
570
|
+
__publicField(this, "_electronApp", null);
|
|
571
|
+
this.cachePath = cachePath;
|
|
572
|
+
this.debug = debug || false;
|
|
573
|
+
this.logger = Logger.getInstance(cachePath);
|
|
367
574
|
this.playwrightPackage = this.installPlaywright();
|
|
368
|
-
this.appWhenReady = createElectronApp(
|
|
369
|
-
params.cachePath,
|
|
370
|
-
this.playwrightPackage
|
|
371
|
-
);
|
|
372
575
|
}
|
|
373
576
|
/**
|
|
374
577
|
* 安装 playwright
|
|
@@ -382,19 +585,54 @@ class LocalAutomateTask {
|
|
|
382
585
|
});
|
|
383
586
|
return playwrightPackageManager.getPluginAfterInit("playwright");
|
|
384
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* 启动 Electron
|
|
590
|
+
* @returns
|
|
591
|
+
*/
|
|
592
|
+
async getElectronApp() {
|
|
593
|
+
if (!this._electronApp) {
|
|
594
|
+
this._electronApp = await createElectronApp(
|
|
595
|
+
this.cachePath,
|
|
596
|
+
this.playwrightPackage
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
return this._electronApp;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* 临时文件目录
|
|
603
|
+
* @returns
|
|
604
|
+
*/
|
|
605
|
+
getTmpPath() {
|
|
606
|
+
return path__default.join(this.cachePath, "tmp");
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* 清空临时文件
|
|
610
|
+
*/
|
|
611
|
+
clearTmpPath() {
|
|
612
|
+
const tmpPath = this.getTmpPath();
|
|
613
|
+
return fs__default.rm(tmpPath, { recursive: true, force: true }, (err) => {
|
|
614
|
+
if (err) {
|
|
615
|
+
this.logger.error("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u5931\u8D25\uFF0C", err);
|
|
616
|
+
} else {
|
|
617
|
+
this.logger.info("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u6210\u529F");
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
385
621
|
/**
|
|
386
622
|
* 关闭 playwright 启动的 electron 客户端
|
|
387
623
|
* @returns
|
|
388
624
|
*/
|
|
389
625
|
async close() {
|
|
390
|
-
|
|
626
|
+
this.logger.close();
|
|
627
|
+
this.clearTmpPath();
|
|
628
|
+
const electronApp = await this.getElectronApp();
|
|
391
629
|
return electronApp.close();
|
|
392
630
|
}
|
|
393
631
|
/**
|
|
394
632
|
* 小红书自动化发布
|
|
395
633
|
*/
|
|
396
634
|
async xiaohongshuPublish(params) {
|
|
397
|
-
const electronApp = await this.
|
|
635
|
+
const electronApp = await this.getElectronApp();
|
|
398
636
|
const commonCookies = {
|
|
399
637
|
path: "/",
|
|
400
638
|
sameSite: "lax",
|
|
@@ -434,22 +672,30 @@ class LocalAutomateTask {
|
|
|
434
672
|
{ pageParams }
|
|
435
673
|
)
|
|
436
674
|
]);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
675
|
+
try {
|
|
676
|
+
const res = await xiaohongshuPublishAction({
|
|
677
|
+
page,
|
|
678
|
+
params,
|
|
679
|
+
tmpCachePath: this.getTmpPath(),
|
|
680
|
+
debug: !!this.debug
|
|
681
|
+
});
|
|
682
|
+
return res;
|
|
683
|
+
} catch (error) {
|
|
684
|
+
this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
444
687
|
}
|
|
445
688
|
}
|
|
446
689
|
const RpaTask = (params) => {
|
|
690
|
+
const logger = Logger.getInstance(params.cachePath);
|
|
447
691
|
const packageManager = new PackageManager({
|
|
448
692
|
packageName: packageJson.name,
|
|
449
|
-
cacheDir: params.cachePath
|
|
693
|
+
cacheDir: params.cachePath,
|
|
694
|
+
forceUpdate: !!params.forceUpdate
|
|
450
695
|
});
|
|
451
696
|
const localPackge = packageManager.getPlugin(packageJson.name);
|
|
452
|
-
if (localPackge?.LocalAutomateTask && localPackge?.version &&
|
|
697
|
+
if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
|
|
698
|
+
logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
|
|
453
699
|
return new localPackge.LocalAutomateTask(params);
|
|
454
700
|
}
|
|
455
701
|
return new LocalAutomateTask(params);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { ElectronApplication } from 'playwright';
|
|
2
1
|
import { CookiesSetDetails } from 'electron';
|
|
3
2
|
|
|
3
|
+
declare class Logger {
|
|
4
|
+
static instance: Logger | null;
|
|
5
|
+
private stream;
|
|
6
|
+
constructor(cachePath: string);
|
|
7
|
+
static getInstance(cachePath: string): Logger;
|
|
8
|
+
debug(...msg: any[]): void;
|
|
9
|
+
info(...msg: any[]): void;
|
|
10
|
+
warn(...msg: any[]): void;
|
|
11
|
+
error(prefix: string, error: unknown): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
type CookieMap = CookiesSetDetails[];
|
|
5
16
|
|
|
6
17
|
interface FictionalRendition {
|
|
@@ -42,18 +53,34 @@ interface XiaohonshuPublishParams
|
|
|
42
53
|
interface TaskParams {
|
|
43
54
|
debug?: boolean;
|
|
44
55
|
cachePath: string;
|
|
56
|
+
forceUpdate?: boolean;
|
|
45
57
|
}
|
|
46
58
|
declare class LocalAutomateTask implements TaskParams {
|
|
47
59
|
cachePath: string;
|
|
48
60
|
debug: boolean;
|
|
49
|
-
appWhenReady: Promise<ElectronApplication>;
|
|
50
61
|
playwrightPackage: Promise<unknown>;
|
|
51
|
-
|
|
62
|
+
logger: Logger;
|
|
63
|
+
private _electronApp;
|
|
64
|
+
constructor({ cachePath, debug }: TaskParams);
|
|
52
65
|
/**
|
|
53
66
|
* 安装 playwright
|
|
54
67
|
* @returns
|
|
55
68
|
*/
|
|
56
|
-
installPlaywright
|
|
69
|
+
private installPlaywright;
|
|
70
|
+
/**
|
|
71
|
+
* 启动 Electron
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
private getElectronApp;
|
|
75
|
+
/**
|
|
76
|
+
* 临时文件目录
|
|
77
|
+
* @returns
|
|
78
|
+
*/
|
|
79
|
+
private getTmpPath;
|
|
80
|
+
/**
|
|
81
|
+
* 清空临时文件
|
|
82
|
+
*/
|
|
83
|
+
private clearTmpPath;
|
|
57
84
|
/**
|
|
58
85
|
* 关闭 playwright 启动的 electron 客户端
|
|
59
86
|
* @returns
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { ElectronApplication } from 'playwright';
|
|
2
1
|
import { CookiesSetDetails } from 'electron';
|
|
3
2
|
|
|
3
|
+
declare class Logger {
|
|
4
|
+
static instance: Logger | null;
|
|
5
|
+
private stream;
|
|
6
|
+
constructor(cachePath: string);
|
|
7
|
+
static getInstance(cachePath: string): Logger;
|
|
8
|
+
debug(...msg: any[]): void;
|
|
9
|
+
info(...msg: any[]): void;
|
|
10
|
+
warn(...msg: any[]): void;
|
|
11
|
+
error(prefix: string, error: unknown): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
type CookieMap = CookiesSetDetails[];
|
|
5
16
|
|
|
6
17
|
interface FictionalRendition {
|
|
@@ -42,18 +53,34 @@ interface XiaohonshuPublishParams
|
|
|
42
53
|
interface TaskParams {
|
|
43
54
|
debug?: boolean;
|
|
44
55
|
cachePath: string;
|
|
56
|
+
forceUpdate?: boolean;
|
|
45
57
|
}
|
|
46
58
|
declare class LocalAutomateTask implements TaskParams {
|
|
47
59
|
cachePath: string;
|
|
48
60
|
debug: boolean;
|
|
49
|
-
appWhenReady: Promise<ElectronApplication>;
|
|
50
61
|
playwrightPackage: Promise<unknown>;
|
|
51
|
-
|
|
62
|
+
logger: Logger;
|
|
63
|
+
private _electronApp;
|
|
64
|
+
constructor({ cachePath, debug }: TaskParams);
|
|
52
65
|
/**
|
|
53
66
|
* 安装 playwright
|
|
54
67
|
* @returns
|
|
55
68
|
*/
|
|
56
|
-
installPlaywright
|
|
69
|
+
private installPlaywright;
|
|
70
|
+
/**
|
|
71
|
+
* 启动 Electron
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
private getElectronApp;
|
|
75
|
+
/**
|
|
76
|
+
* 临时文件目录
|
|
77
|
+
* @returns
|
|
78
|
+
*/
|
|
79
|
+
private getTmpPath;
|
|
80
|
+
/**
|
|
81
|
+
* 清空临时文件
|
|
82
|
+
*/
|
|
83
|
+
private clearTmpPath;
|
|
57
84
|
/**
|
|
58
85
|
* 关闭 playwright 启动的 electron 客户端
|
|
59
86
|
* @returns
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { ElectronApplication } from 'playwright';
|
|
2
1
|
import { CookiesSetDetails } from 'electron';
|
|
3
2
|
|
|
3
|
+
declare class Logger {
|
|
4
|
+
static instance: Logger | null;
|
|
5
|
+
private stream;
|
|
6
|
+
constructor(cachePath: string);
|
|
7
|
+
static getInstance(cachePath: string): Logger;
|
|
8
|
+
debug(...msg: any[]): void;
|
|
9
|
+
info(...msg: any[]): void;
|
|
10
|
+
warn(...msg: any[]): void;
|
|
11
|
+
error(prefix: string, error: unknown): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
type CookieMap = CookiesSetDetails[];
|
|
5
16
|
|
|
6
17
|
interface FictionalRendition {
|
|
@@ -42,18 +53,34 @@ interface XiaohonshuPublishParams
|
|
|
42
53
|
interface TaskParams {
|
|
43
54
|
debug?: boolean;
|
|
44
55
|
cachePath: string;
|
|
56
|
+
forceUpdate?: boolean;
|
|
45
57
|
}
|
|
46
58
|
declare class LocalAutomateTask implements TaskParams {
|
|
47
59
|
cachePath: string;
|
|
48
60
|
debug: boolean;
|
|
49
|
-
appWhenReady: Promise<ElectronApplication>;
|
|
50
61
|
playwrightPackage: Promise<unknown>;
|
|
51
|
-
|
|
62
|
+
logger: Logger;
|
|
63
|
+
private _electronApp;
|
|
64
|
+
constructor({ cachePath, debug }: TaskParams);
|
|
52
65
|
/**
|
|
53
66
|
* 安装 playwright
|
|
54
67
|
* @returns
|
|
55
68
|
*/
|
|
56
|
-
installPlaywright
|
|
69
|
+
private installPlaywright;
|
|
70
|
+
/**
|
|
71
|
+
* 启动 Electron
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
private getElectronApp;
|
|
75
|
+
/**
|
|
76
|
+
* 临时文件目录
|
|
77
|
+
* @returns
|
|
78
|
+
*/
|
|
79
|
+
private getTmpPath;
|
|
80
|
+
/**
|
|
81
|
+
* 清空临时文件
|
|
82
|
+
*/
|
|
83
|
+
private clearTmpPath;
|
|
57
84
|
/**
|
|
58
85
|
* 关闭 playwright 启动的 electron 客户端
|
|
59
86
|
* @returns
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import spawn from 'cross-spawn';
|
|
3
|
+
import https from 'node:https';
|
|
4
|
+
import log from 'loglevel';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
7
6
|
|
|
8
7
|
const name = "@iflyrpa/playwright";
|
|
9
8
|
const type = "module";
|
|
10
|
-
const version$1 = "1.0.
|
|
9
|
+
const version$1 = "1.0.10-beta.0";
|
|
11
10
|
const description = "";
|
|
12
11
|
const main = "./dist/index.cjs";
|
|
13
12
|
const module = "./dist/index.mjs";
|
|
@@ -25,23 +24,14 @@ const files = [
|
|
|
25
24
|
"dist"
|
|
26
25
|
];
|
|
27
26
|
const dependencies = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
deepmerge: "^4.3.1",
|
|
31
|
-
"fs-extra": "^11.2.0",
|
|
32
|
-
playwright: "^1.46.1",
|
|
33
|
-
semver: "^7.6.3"
|
|
27
|
+
loglevel: "^1.9.2",
|
|
28
|
+
playwright: "^1.46.1"
|
|
34
29
|
};
|
|
35
30
|
const peerDependencies = {
|
|
36
31
|
electron: "*"
|
|
37
32
|
};
|
|
38
33
|
const devDependencies = {
|
|
39
|
-
"@types/cross-spawn": "^6.0.6",
|
|
40
|
-
"@types/fs-extra": "^11.0.4",
|
|
41
|
-
"@types/semver": "^7.5.8",
|
|
42
|
-
bumpp: "^9.5.2",
|
|
43
34
|
esno: "^4.7.0",
|
|
44
|
-
"ts-node": "^10.9.2",
|
|
45
35
|
typescript: "^5.5.2",
|
|
46
36
|
unbuild: "^2.0.0"
|
|
47
37
|
};
|
|
@@ -63,32 +53,166 @@ const packageJson = {
|
|
|
63
53
|
devDependencies: devDependencies
|
|
64
54
|
};
|
|
65
55
|
|
|
66
|
-
async function downloadImage(url,
|
|
67
|
-
|
|
68
|
-
url,
|
|
69
|
-
method: "GET",
|
|
70
|
-
responseType: "stream"
|
|
71
|
-
// 重要:设置响应类型为 'stream'
|
|
72
|
-
});
|
|
73
|
-
await fs.ensureFile(path2);
|
|
74
|
-
const writer = fs.createWriteStream(path2);
|
|
75
|
-
response.data.pipe(writer);
|
|
56
|
+
async function downloadImage(url, savePath) {
|
|
57
|
+
await ensureFile(savePath);
|
|
76
58
|
return new Promise((resolve, reject) => {
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
https.get(url, (response) => {
|
|
60
|
+
if (response.statusCode === 200) {
|
|
61
|
+
const fileStream = fs.createWriteStream(savePath);
|
|
62
|
+
response.pipe(fileStream);
|
|
63
|
+
fileStream.on("finish", () => {
|
|
64
|
+
fileStream.close();
|
|
65
|
+
console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
|
|
66
|
+
resolve(savePath);
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
|
|
70
|
+
response.resume();
|
|
71
|
+
reject();
|
|
72
|
+
}
|
|
73
|
+
}).on("error", (error) => {
|
|
74
|
+
console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
|
|
75
|
+
reject();
|
|
76
|
+
});
|
|
79
77
|
});
|
|
80
78
|
}
|
|
81
79
|
function getFilenameFromUrl(imageUrl) {
|
|
82
80
|
const parsedUrl = new URL(imageUrl);
|
|
83
81
|
return path.basename(parsedUrl.pathname);
|
|
84
82
|
}
|
|
83
|
+
function pathExists(path2) {
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
fs.stat(path2, (err) => {
|
|
86
|
+
resolve(!err);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function ensureFile(filePath) {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const dirPath = path.dirname(filePath);
|
|
93
|
+
fs.stat(dirPath, (err, stats) => {
|
|
94
|
+
if (err) {
|
|
95
|
+
if (err.code === "ENOENT") {
|
|
96
|
+
fs.mkdir(dirPath, { recursive: true }, (err2) => {
|
|
97
|
+
if (err2) {
|
|
98
|
+
return reject(err2);
|
|
99
|
+
}
|
|
100
|
+
createFile();
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
return reject(err);
|
|
104
|
+
}
|
|
105
|
+
} else if (stats.isDirectory()) {
|
|
106
|
+
checkFile();
|
|
107
|
+
} else {
|
|
108
|
+
reject(new Error(`${dirPath} is not a directory`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
function checkFile() {
|
|
112
|
+
fs.stat(filePath, (err, stats) => {
|
|
113
|
+
if (err) {
|
|
114
|
+
if (err.code === "ENOENT") {
|
|
115
|
+
createFile();
|
|
116
|
+
} else {
|
|
117
|
+
reject(err);
|
|
118
|
+
}
|
|
119
|
+
} else if (stats.isFile()) {
|
|
120
|
+
resolve();
|
|
121
|
+
} else {
|
|
122
|
+
reject(new Error(`${filePath} is not a file`));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function createFile() {
|
|
127
|
+
fs.writeFile(filePath, "", (err) => {
|
|
128
|
+
if (err) {
|
|
129
|
+
reject(err);
|
|
130
|
+
} else {
|
|
131
|
+
resolve();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function ensureFileSync(filePath) {
|
|
138
|
+
const dirPath = path.dirname(filePath);
|
|
139
|
+
try {
|
|
140
|
+
if (!fs.existsSync(dirPath)) {
|
|
141
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err instanceof Error) {
|
|
145
|
+
throw new Error(`Error creating directory: ${err.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
if (!fs.existsSync(filePath)) {
|
|
150
|
+
fs.writeFileSync(filePath, "");
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err instanceof Error) {
|
|
154
|
+
throw new Error(`Error creating file: ${err.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function writeFile(filePath, data) {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
fs.writeFile(filePath, data, (err) => {
|
|
161
|
+
if (err) {
|
|
162
|
+
console.error("Error writing file:", err);
|
|
163
|
+
reject();
|
|
164
|
+
} else {
|
|
165
|
+
console.log("File written successfully");
|
|
166
|
+
resolve();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function compareVersions(v1, v2) {
|
|
172
|
+
const parts1 = v1.split(".").map(Number);
|
|
173
|
+
const parts2 = v2.split(".").map(Number);
|
|
174
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
175
|
+
const num1 = i < parts1.length ? parts1[i] : 0;
|
|
176
|
+
const num2 = i < parts2.length ? parts2[i] : 0;
|
|
177
|
+
if (num1 > num2)
|
|
178
|
+
return 1;
|
|
179
|
+
if (num1 < num2)
|
|
180
|
+
return -1;
|
|
181
|
+
}
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
const semver = {
|
|
185
|
+
gt: (v1, v2) => compareVersions(v1, v2) === 1
|
|
186
|
+
};
|
|
187
|
+
function fetchJSON(url) {
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
https.get(url, (res) => {
|
|
190
|
+
let data = "";
|
|
191
|
+
res.on("data", (chunk) => {
|
|
192
|
+
data += chunk;
|
|
193
|
+
});
|
|
194
|
+
res.on("end", () => {
|
|
195
|
+
try {
|
|
196
|
+
const parsedData = JSON.parse(data);
|
|
197
|
+
resolve(parsedData);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
if (e instanceof Error) {
|
|
200
|
+
reject(new Error(`Error parsing JSON: ${e.message}`));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}).on("error", (err) => {
|
|
205
|
+
reject(new Error(`Request failed: ${err.message}`));
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
85
209
|
|
|
86
210
|
const visibleRangeTexts = {
|
|
87
211
|
public: "\u516C\u5F00",
|
|
88
212
|
private: "\u79C1\u5BC6"
|
|
89
213
|
};
|
|
90
214
|
const xiaohongshuPublishAction = async (props) => {
|
|
91
|
-
const { page,
|
|
215
|
+
const { page, tmpCachePath, params } = props;
|
|
92
216
|
const selectAddress = async (selector, address) => {
|
|
93
217
|
const instance = typeof selector === "string" ? page.locator(selector) : selector;
|
|
94
218
|
await instance.click();
|
|
@@ -111,11 +235,13 @@ const xiaohongshuPublishAction = async (props) => {
|
|
|
111
235
|
await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
|
|
112
236
|
throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
|
|
113
237
|
});
|
|
114
|
-
await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click()
|
|
238
|
+
await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click().catch(() => {
|
|
239
|
+
throw new Error("\u672A\u627E\u5230\u4E0A\u4F20\u56FE\u6587\u6309\u94AE");
|
|
240
|
+
});
|
|
115
241
|
const images = await Promise.all(
|
|
116
242
|
params.banners.map((url) => {
|
|
117
243
|
const fileName = getFilenameFromUrl(url);
|
|
118
|
-
return downloadImage(url, path.join(
|
|
244
|
+
return downloadImage(url, path.join(tmpCachePath, fileName));
|
|
119
245
|
})
|
|
120
246
|
);
|
|
121
247
|
const fileChooserPromise = page.waitForEvent("filechooser");
|
|
@@ -205,6 +331,64 @@ const xiaohongshuPublishAction = async (props) => {
|
|
|
205
331
|
return response;
|
|
206
332
|
};
|
|
207
333
|
|
|
334
|
+
var __defProp$2 = Object.defineProperty;
|
|
335
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
336
|
+
var __publicField$2 = (obj, key, value) => {
|
|
337
|
+
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
338
|
+
return value;
|
|
339
|
+
};
|
|
340
|
+
const _Logger = class _Logger {
|
|
341
|
+
constructor(cachePath) {
|
|
342
|
+
__publicField$2(this, "stream");
|
|
343
|
+
if (_Logger.instance) {
|
|
344
|
+
return _Logger.instance;
|
|
345
|
+
}
|
|
346
|
+
const logFile = path.join(cachePath, "rpa.log");
|
|
347
|
+
this.stream = fs.createWriteStream(logFile, { flags: "a" });
|
|
348
|
+
log.setLevel("debug");
|
|
349
|
+
log.methodFactory = (methodName) => {
|
|
350
|
+
return (message) => {
|
|
351
|
+
this.stream.write(
|
|
352
|
+
`[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
|
|
353
|
+
`
|
|
354
|
+
);
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
log.setLevel(log.getLevel());
|
|
358
|
+
_Logger.instance = this;
|
|
359
|
+
}
|
|
360
|
+
static getInstance(cachePath) {
|
|
361
|
+
if (!_Logger.instance) {
|
|
362
|
+
_Logger.instance = new _Logger(cachePath);
|
|
363
|
+
}
|
|
364
|
+
return _Logger.instance;
|
|
365
|
+
}
|
|
366
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
367
|
+
debug(...msg) {
|
|
368
|
+
log.debug(...msg);
|
|
369
|
+
}
|
|
370
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
371
|
+
info(...msg) {
|
|
372
|
+
log.info(...msg);
|
|
373
|
+
}
|
|
374
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
375
|
+
warn(...msg) {
|
|
376
|
+
log.warn(...msg);
|
|
377
|
+
}
|
|
378
|
+
error(prefix, error) {
|
|
379
|
+
log.error(prefix);
|
|
380
|
+
if (error instanceof Error) {
|
|
381
|
+
log.error(`${error.message}
|
|
382
|
+
Error stack: ${error.stack}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
close() {
|
|
386
|
+
this.stream.end();
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
__publicField$2(_Logger, "instance", null);
|
|
390
|
+
let Logger = _Logger;
|
|
391
|
+
|
|
208
392
|
const template = `
|
|
209
393
|
const { app } = require("electron");
|
|
210
394
|
|
|
@@ -214,22 +398,35 @@ app.on("window-all-closed", (e) => e.preventDefault());
|
|
|
214
398
|
`;
|
|
215
399
|
const generateFile = async (dir) => {
|
|
216
400
|
const filePath = path.join(dir, "src", "main.js");
|
|
217
|
-
const
|
|
218
|
-
if (!
|
|
219
|
-
await
|
|
220
|
-
await
|
|
401
|
+
const isPathExists = await pathExists(filePath);
|
|
402
|
+
if (!isPathExists) {
|
|
403
|
+
await ensureFile(filePath);
|
|
404
|
+
await writeFile(filePath, template);
|
|
221
405
|
}
|
|
222
406
|
return filePath;
|
|
223
407
|
};
|
|
224
408
|
const createElectronApp = async (cachePath, playwrightPackage) => {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
409
|
+
const logger = Logger.getInstance(cachePath);
|
|
410
|
+
try {
|
|
411
|
+
const executablePath = path.join(
|
|
412
|
+
cachePath,
|
|
413
|
+
"node_modules",
|
|
414
|
+
".bin",
|
|
415
|
+
"electron"
|
|
416
|
+
);
|
|
417
|
+
const playwright = await playwrightPackage;
|
|
418
|
+
const mainPath = await generateFile(cachePath);
|
|
419
|
+
const electronApp = await playwright._electron.launch({
|
|
420
|
+
executablePath,
|
|
421
|
+
// 获取 Electron 可执行文件的路径
|
|
422
|
+
args: [mainPath],
|
|
423
|
+
cwd: cachePath
|
|
424
|
+
});
|
|
425
|
+
logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
|
|
426
|
+
return electronApp;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
|
|
429
|
+
}
|
|
233
430
|
};
|
|
234
431
|
|
|
235
432
|
var __defProp$1 = Object.defineProperty;
|
|
@@ -245,8 +442,11 @@ class PackageManager {
|
|
|
245
442
|
__publicField$1(this, "forceUpdate");
|
|
246
443
|
// 是否强制更新
|
|
247
444
|
__publicField$1(this, "initPromise");
|
|
445
|
+
__publicField$1(this, "logger");
|
|
446
|
+
this.logger = Logger.getInstance(params.cacheDir);
|
|
248
447
|
this.cacheDir = params.cacheDir;
|
|
249
448
|
this.forceUpdate = params.forceUpdate || true;
|
|
449
|
+
this.initCacheProject();
|
|
250
450
|
this.initPromise = this.init(params.packageName, params.packageVersion);
|
|
251
451
|
}
|
|
252
452
|
// 在子线程执行 npm 命令
|
|
@@ -254,7 +454,8 @@ class PackageManager {
|
|
|
254
454
|
return new Promise((resolve) => {
|
|
255
455
|
const args = [cmd].concat(modules).concat("--color=always").concat("--save");
|
|
256
456
|
try {
|
|
257
|
-
const
|
|
457
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
458
|
+
const npm = spawn(npmCmd, args, { cwd: where });
|
|
258
459
|
let output = "";
|
|
259
460
|
npm.stdout?.on("data", (data) => {
|
|
260
461
|
output += data;
|
|
@@ -270,11 +471,10 @@ class PackageManager {
|
|
|
270
471
|
}
|
|
271
472
|
});
|
|
272
473
|
npm.on("error", (err) => {
|
|
273
|
-
|
|
274
|
-
console.error(err);
|
|
474
|
+
this.logger.error("npm run error", err);
|
|
275
475
|
});
|
|
276
476
|
} catch (error) {
|
|
277
|
-
|
|
477
|
+
this.logger.error(`npm ${args.join(" ")}: `, error);
|
|
278
478
|
}
|
|
279
479
|
});
|
|
280
480
|
}
|
|
@@ -282,21 +482,23 @@ class PackageManager {
|
|
|
282
482
|
initCacheProject() {
|
|
283
483
|
const packagePath = path.join(this.cacheDir, "package.json");
|
|
284
484
|
if (!fs.existsSync(packagePath)) {
|
|
485
|
+
this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
|
|
285
486
|
const pkg = {
|
|
286
487
|
name: "rpa-plugins",
|
|
287
488
|
description: "rpa-plugins",
|
|
288
|
-
license: "MIT"
|
|
489
|
+
license: "MIT",
|
|
490
|
+
main: "./src/main.js"
|
|
289
491
|
};
|
|
290
|
-
|
|
492
|
+
ensureFileSync(packagePath);
|
|
291
493
|
fs.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
|
|
292
494
|
}
|
|
293
495
|
}
|
|
294
496
|
// 获取依赖信息
|
|
295
497
|
async getPluginInfo(module) {
|
|
296
|
-
const res = await
|
|
498
|
+
const res = await fetchJSON(
|
|
297
499
|
`https://registry.npmjs.com/-/v1/search?text=${module}`
|
|
298
500
|
);
|
|
299
|
-
const packages = res.
|
|
501
|
+
const packages = res.objects;
|
|
300
502
|
return packages.find((it) => it.package.name === module)?.package;
|
|
301
503
|
}
|
|
302
504
|
// 查询本地安装的依赖
|
|
@@ -305,6 +507,7 @@ class PackageManager {
|
|
|
305
507
|
try {
|
|
306
508
|
return require(path.join(pluginDir, module));
|
|
307
509
|
} catch (error) {
|
|
510
|
+
this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
|
|
308
511
|
return null;
|
|
309
512
|
}
|
|
310
513
|
}
|
|
@@ -322,20 +525,23 @@ class PackageManager {
|
|
|
322
525
|
return this.execCommand("update", [module]);
|
|
323
526
|
}
|
|
324
527
|
async init(name, version) {
|
|
325
|
-
this.initCacheProject();
|
|
326
528
|
const plugin = this.getPlugin(path.join(name, "package.json"));
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
529
|
+
try {
|
|
530
|
+
if (!plugin) {
|
|
531
|
+
await this.install(name, version);
|
|
532
|
+
} else if (this.forceUpdate) {
|
|
533
|
+
const pkInfo = await this.getPluginInfo(name);
|
|
534
|
+
if (!pkInfo)
|
|
535
|
+
return;
|
|
536
|
+
const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
|
|
537
|
+
if (hasNewVersion) {
|
|
538
|
+
await this.install(name, pkInfo.version);
|
|
539
|
+
}
|
|
336
540
|
}
|
|
541
|
+
this.logger.info(`${name} package manager init done!`);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
this.logger.error(`${name} package manager init error`, error);
|
|
337
544
|
}
|
|
338
|
-
console.log("Package manager init done!");
|
|
339
545
|
}
|
|
340
546
|
}
|
|
341
547
|
|
|
@@ -347,18 +553,16 @@ var __publicField = (obj, key, value) => {
|
|
|
347
553
|
};
|
|
348
554
|
const PLAYWRIGHT_VERSION = "1.46.1";
|
|
349
555
|
class LocalAutomateTask {
|
|
350
|
-
constructor(
|
|
556
|
+
constructor({ cachePath, debug }) {
|
|
351
557
|
__publicField(this, "cachePath");
|
|
352
558
|
__publicField(this, "debug");
|
|
353
|
-
__publicField(this, "appWhenReady");
|
|
354
559
|
__publicField(this, "playwrightPackage");
|
|
355
|
-
this
|
|
356
|
-
this
|
|
560
|
+
__publicField(this, "logger");
|
|
561
|
+
__publicField(this, "_electronApp", null);
|
|
562
|
+
this.cachePath = cachePath;
|
|
563
|
+
this.debug = debug || false;
|
|
564
|
+
this.logger = Logger.getInstance(cachePath);
|
|
357
565
|
this.playwrightPackage = this.installPlaywright();
|
|
358
|
-
this.appWhenReady = createElectronApp(
|
|
359
|
-
params.cachePath,
|
|
360
|
-
this.playwrightPackage
|
|
361
|
-
);
|
|
362
566
|
}
|
|
363
567
|
/**
|
|
364
568
|
* 安装 playwright
|
|
@@ -372,19 +576,54 @@ class LocalAutomateTask {
|
|
|
372
576
|
});
|
|
373
577
|
return playwrightPackageManager.getPluginAfterInit("playwright");
|
|
374
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* 启动 Electron
|
|
581
|
+
* @returns
|
|
582
|
+
*/
|
|
583
|
+
async getElectronApp() {
|
|
584
|
+
if (!this._electronApp) {
|
|
585
|
+
this._electronApp = await createElectronApp(
|
|
586
|
+
this.cachePath,
|
|
587
|
+
this.playwrightPackage
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return this._electronApp;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* 临时文件目录
|
|
594
|
+
* @returns
|
|
595
|
+
*/
|
|
596
|
+
getTmpPath() {
|
|
597
|
+
return path.join(this.cachePath, "tmp");
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* 清空临时文件
|
|
601
|
+
*/
|
|
602
|
+
clearTmpPath() {
|
|
603
|
+
const tmpPath = this.getTmpPath();
|
|
604
|
+
return fs.rm(tmpPath, { recursive: true, force: true }, (err) => {
|
|
605
|
+
if (err) {
|
|
606
|
+
this.logger.error("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u5931\u8D25\uFF0C", err);
|
|
607
|
+
} else {
|
|
608
|
+
this.logger.info("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u6210\u529F");
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
375
612
|
/**
|
|
376
613
|
* 关闭 playwright 启动的 electron 客户端
|
|
377
614
|
* @returns
|
|
378
615
|
*/
|
|
379
616
|
async close() {
|
|
380
|
-
|
|
617
|
+
this.logger.close();
|
|
618
|
+
this.clearTmpPath();
|
|
619
|
+
const electronApp = await this.getElectronApp();
|
|
381
620
|
return electronApp.close();
|
|
382
621
|
}
|
|
383
622
|
/**
|
|
384
623
|
* 小红书自动化发布
|
|
385
624
|
*/
|
|
386
625
|
async xiaohongshuPublish(params) {
|
|
387
|
-
const electronApp = await this.
|
|
626
|
+
const electronApp = await this.getElectronApp();
|
|
388
627
|
const commonCookies = {
|
|
389
628
|
path: "/",
|
|
390
629
|
sameSite: "lax",
|
|
@@ -424,22 +663,30 @@ class LocalAutomateTask {
|
|
|
424
663
|
{ pageParams }
|
|
425
664
|
)
|
|
426
665
|
]);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
666
|
+
try {
|
|
667
|
+
const res = await xiaohongshuPublishAction({
|
|
668
|
+
page,
|
|
669
|
+
params,
|
|
670
|
+
tmpCachePath: this.getTmpPath(),
|
|
671
|
+
debug: !!this.debug
|
|
672
|
+
});
|
|
673
|
+
return res;
|
|
674
|
+
} catch (error) {
|
|
675
|
+
this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
434
678
|
}
|
|
435
679
|
}
|
|
436
680
|
const RpaTask = (params) => {
|
|
681
|
+
const logger = Logger.getInstance(params.cachePath);
|
|
437
682
|
const packageManager = new PackageManager({
|
|
438
683
|
packageName: packageJson.name,
|
|
439
|
-
cacheDir: params.cachePath
|
|
684
|
+
cacheDir: params.cachePath,
|
|
685
|
+
forceUpdate: !!params.forceUpdate
|
|
440
686
|
});
|
|
441
687
|
const localPackge = packageManager.getPlugin(packageJson.name);
|
|
442
688
|
if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
|
|
689
|
+
logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
|
|
443
690
|
return new localPackge.LocalAutomateTask(params);
|
|
444
691
|
}
|
|
445
692
|
return new LocalAutomateTask(params);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iflyrpa/playwright",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.10-beta.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -16,23 +16,14 @@
|
|
|
16
16
|
"license": "ISC",
|
|
17
17
|
"files": ["dist"],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"deepmerge": "^4.3.1",
|
|
22
|
-
"fs-extra": "^11.2.0",
|
|
23
|
-
"playwright": "^1.46.1",
|
|
24
|
-
"semver": "^7.6.3"
|
|
19
|
+
"loglevel": "^1.9.2",
|
|
20
|
+
"playwright": "^1.46.1"
|
|
25
21
|
},
|
|
26
22
|
"peerDependencies": {
|
|
27
23
|
"electron": "*"
|
|
28
24
|
},
|
|
29
25
|
"devDependencies": {
|
|
30
|
-
"@types/cross-spawn": "^6.0.6",
|
|
31
|
-
"@types/fs-extra": "^11.0.4",
|
|
32
|
-
"@types/semver": "^7.5.8",
|
|
33
|
-
"bumpp": "^9.5.2",
|
|
34
26
|
"esno": "^4.7.0",
|
|
35
|
-
"ts-node": "^10.9.2",
|
|
36
27
|
"typescript": "^5.5.2",
|
|
37
28
|
"unbuild": "^2.0.0"
|
|
38
29
|
}
|