mirror of
https://github.com/NaiboWang/EasySpider.git
synced 2025-04-20 04:39:57 +08:00
1459 lines
50 KiB
JavaScript
1459 lines
50 KiB
JavaScript
// Modules to control application life and create native browser window
|
||
const {
|
||
app,
|
||
BrowserWindow,
|
||
dialog,
|
||
ipcMain,
|
||
screen,
|
||
session,
|
||
} = require("electron");
|
||
app.commandLine.appendSwitch("--disable-http-cache");
|
||
const {
|
||
Builder,
|
||
By,
|
||
Key,
|
||
until,
|
||
Select,
|
||
StaleElementReferenceException,
|
||
} = require("selenium-webdriver");
|
||
const chrome = require("selenium-webdriver/chrome");
|
||
const { ServiceBuilder } = require("selenium-webdriver/chrome");
|
||
const { rootCertificates } = require("tls");
|
||
const { exit } = require("process");
|
||
const path = require("path");
|
||
const fs = require("fs");
|
||
const { exec, spawn, execFile } = require("child_process");
|
||
const iconPath = path.join(__dirname, "favicon.ico");
|
||
const task_server = require(path.join(__dirname, "server.js"));
|
||
const util = require("util");
|
||
|
||
let config = fs.readFileSync(
|
||
path.join(task_server.getDir(), `config.json`),
|
||
"utf8"
|
||
);
|
||
config = JSON.parse(config);
|
||
let config_context = JSON.parse(
|
||
fs.readFileSync(path.join(task_server.getDir(), `config.json`), "utf8")
|
||
); //仅在当前进程中使用,不会写入文件
|
||
|
||
if (config.debug) {
|
||
let logPath = "info.log";
|
||
let logFile = fs.createWriteStream(logPath, { flags: "a" });
|
||
console.log = function () {
|
||
logFile.write(util.format.apply(null, arguments) + "\n");
|
||
process.stdout.write(util.format.apply(null, arguments) + "\n");
|
||
};
|
||
console.error = function () {
|
||
logFile.write(util.format.apply(null, arguments) + "\n");
|
||
process.stderr.write(util.format.apply(null, arguments) + "\n");
|
||
};
|
||
}
|
||
let allWindowSockets = [];
|
||
let allWindowScoketNames = [];
|
||
task_server.start(config.webserver_port); //start local server
|
||
let server_address = `${config.webserver_address}:${config.webserver_port}`;
|
||
const websocket_port = 8084; //目前只支持8084端口,写死,因为扩展里面写死了
|
||
console.log("server_address: " + server_address);
|
||
let driverPath = "";
|
||
let chromeBinaryPath = "";
|
||
let execute_path = "";
|
||
console.log(process.arch);
|
||
|
||
exec(`wmic os get Caption`, function (error, stdout, stderr) {
|
||
if (error) {
|
||
console.error(`执行的错误: ${error}`);
|
||
return;
|
||
}
|
||
|
||
if (stdout.includes("Windows 7")) {
|
||
console.log("Windows 7");
|
||
let sys_arch = config.sys_arch;
|
||
if (sys_arch === "x64") {
|
||
dialog.showMessageBoxSync({
|
||
type: "error",
|
||
title: "Error",
|
||
message:
|
||
"Windows 7系统请下载使用x32版本的软件,不论Win 7系统为x64还是x32版本。\nFor Windows 7, please download and use the x32 version of the software, regardless of whether the Win 7 system is x64 or x32 version.",
|
||
});
|
||
}
|
||
} else {
|
||
console.log("Not Windows 7");
|
||
}
|
||
});
|
||
|
||
if (process.platform === "win32" && process.arch === "ia32") {
|
||
driverPath = path.join(__dirname, "chrome_win32/chromedriver_win32.exe");
|
||
chromeBinaryPath = path.join(__dirname, "chrome_win32/chrome.exe");
|
||
execute_path = path.join(__dirname, "chrome_win32/execute.bat");
|
||
} else if (process.platform === "win32" && process.arch === "x64") {
|
||
driverPath = path.join(__dirname, "chrome_win64/chromedriver_win64.exe");
|
||
chromeBinaryPath = path.join(__dirname, "chrome_win64/chrome.exe");
|
||
execute_path = path.join(__dirname, "chrome_win64/execute.bat");
|
||
} else if (process.platform === "darwin") {
|
||
driverPath = path.join(__dirname, "chromedriver_mac64");
|
||
chromeBinaryPath = path.join(
|
||
__dirname,
|
||
"chrome_mac64.app/Contents/MacOS/Google Chrome"
|
||
);
|
||
execute_path = path.join(__dirname, "");
|
||
} else if (process.platform === "linux") {
|
||
driverPath = path.join(__dirname, "chrome_linux64/chromedriver_linux64");
|
||
chromeBinaryPath = path.join(__dirname, "chrome_linux64/chrome");
|
||
execute_path = path.join(__dirname, "chrome_linux64/execute.sh");
|
||
}
|
||
console.log(driverPath, chromeBinaryPath, execute_path);
|
||
let language = "en";
|
||
let driver = null;
|
||
let mainWindow = null;
|
||
let flowchart_window = null;
|
||
let current_handle = null;
|
||
let old_handles = [];
|
||
let handle_pairs = {};
|
||
let socket_window = null;
|
||
let socket_start = null;
|
||
let socket_flowchart = null;
|
||
let invoke_window = null;
|
||
|
||
// var ffi = require('ffi-napi');
|
||
// var libm = ffi.Library('libm', {
|
||
// 'ceil': [ 'double', [ 'double' ] ]
|
||
// });
|
||
// libm.ceil(1.5); // 2
|
||
// const {user32FindWindowEx,
|
||
// winspoolGetDefaultPrinter,} = require('win32-api/fun');
|
||
// async function testt(){
|
||
// // 获取当前电脑当前用户默认打印机名
|
||
// const printerName = await winspoolGetDefaultPrinter()
|
||
// console.log(printerName);
|
||
// }
|
||
|
||
// testt();
|
||
|
||
function createWindow() {
|
||
// Create the browser window.
|
||
mainWindow = new BrowserWindow({
|
||
width: 600,
|
||
height: 800,
|
||
webPreferences: {
|
||
preload: path.join(__dirname, "src/js/preload.js"),
|
||
},
|
||
icon: iconPath,
|
||
// frame: false, //取消window自带的关闭最小化等
|
||
resizable: false, //禁止改变主窗口尺寸
|
||
});
|
||
|
||
// and load the index.html of the app.
|
||
// mainWindow.loadFile('src/index.html');
|
||
mainWindow.loadURL(
|
||
server_address +
|
||
"/index.html?user_data_folder=" +
|
||
config.user_data_folder +
|
||
"©right=" +
|
||
config.copyright,
|
||
{ extraHeaders: "pragma: no-cache\n" }
|
||
);
|
||
// 隐藏菜单栏
|
||
const { Menu } = require("electron");
|
||
Menu.setApplicationMenu(null);
|
||
mainWindow.on("close", function (e) {
|
||
if (process.platform !== "darwin") {
|
||
app.quit();
|
||
}
|
||
});
|
||
// mainWindow.webContents.openDevTools();
|
||
// Open the DevTools.
|
||
// mainWindow.webContents.openDevTools()
|
||
}
|
||
|
||
async function findElementRecursive(driver, by, value, frames) {
|
||
for (const frame of frames) {
|
||
try {
|
||
// Try to switch to the frame
|
||
try {
|
||
await driver.switchTo().frame(frame);
|
||
} catch (error) {
|
||
if (error.name.indexOf("StaleElement") >= 0) {
|
||
// If the frame is stale, switch to the parent frame and then retry switching to the frame
|
||
await driver.switchTo().parentFrame();
|
||
await driver.switchTo().frame(frame);
|
||
} else {
|
||
// If it is another exception rethrow it
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
let element;
|
||
try {
|
||
// Attempt to find the element in this frame
|
||
element = await driver.findElement(by(value));
|
||
return element;
|
||
} catch (error) {
|
||
if (error.name.indexOf("NoSuchElement") >= 0) {
|
||
// The element was not found in this frame, recurse into nested iframes
|
||
const nestedFrames = await driver.findElements(By.tagName("iframe"));
|
||
if (nestedFrames.length > 0) {
|
||
element = await findElementRecursive(
|
||
driver,
|
||
by,
|
||
value,
|
||
nestedFrames
|
||
);
|
||
if (element) {
|
||
return element;
|
||
}
|
||
}
|
||
} else {
|
||
// If it is another exception, log it
|
||
console.error(`Exception while processing frame: ${error}`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error(`Exception while processing frame: ${error}`);
|
||
}
|
||
}
|
||
|
||
throw new Error(`Element ${value} not found in any frame or iframe`);
|
||
}
|
||
|
||
async function findElement(driver, by, value, iframe = false) {
|
||
// Switch back to the main document
|
||
await driver.switchTo().defaultContent();
|
||
|
||
if (iframe) {
|
||
const frames = await driver.findElements(By.tagName("iframe"));
|
||
if (frames.length === 0) {
|
||
throw new Error(
|
||
`No iframes found in the current page while searching for ${value}`
|
||
);
|
||
}
|
||
const element = await findElementRecursive(driver, by, value, frames);
|
||
return element;
|
||
} else {
|
||
// Find element in the main document as normal
|
||
let element = await driver.findElement(by(value));
|
||
return element;
|
||
}
|
||
}
|
||
|
||
async function findElementAcrossAllWindows(
|
||
msg,
|
||
notifyBrowser = true,
|
||
scrollIntoView = true
|
||
) {
|
||
let handles = await driver.getAllWindowHandles();
|
||
// console.log("handles", handles);
|
||
let content_handle = current_handle;
|
||
let id = -1;
|
||
try {
|
||
id = msg.message.id;
|
||
} catch {
|
||
id = msg.id;
|
||
}
|
||
if (id == -1) {
|
||
//如果是-1,从当前窗口开始搜索
|
||
content_handle = current_handle;
|
||
} else {
|
||
content_handle = handle_pairs[id];
|
||
}
|
||
// console.log(msg.message.id, content_handle);
|
||
let order = [
|
||
...handles.filter(
|
||
(handle) => handle != current_handle && handle != content_handle
|
||
),
|
||
current_handle,
|
||
content_handle,
|
||
]; //搜索顺序
|
||
let len = order.length;
|
||
let element = null;
|
||
let iframe = false;
|
||
try {
|
||
iframe = msg.message.iframe;
|
||
} catch {
|
||
iframe = msg.iframe;
|
||
}
|
||
// if (iframe) {
|
||
// notify_browser("在IFrame中执行操作可能需要较长时间,请耐心等待。", "Executing operations in IFrame may take a long time, please wait patiently.", "info");
|
||
// }
|
||
let xpath = "";
|
||
try {
|
||
xpath = msg.message.xpath;
|
||
} catch {
|
||
//如果msg.pathList存在,说明是循环中的元素
|
||
if (
|
||
msg.pathList != undefined &&
|
||
msg.pathList != null &&
|
||
msg.pathList != ""
|
||
) {
|
||
xpath = msg.pathList[0].trim();
|
||
} else {
|
||
xpath = msg.xpath;
|
||
}
|
||
}
|
||
if (xpath.indexOf("Field(") >= 0 || xpath.indexOf("eval(") >= 0) {
|
||
//两秒后通知浏览器
|
||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||
notify_browser(
|
||
'检测到XPath中包含Field("")或eval(""),试运行时无法正常定位到包含此两项表达式的元素,请在任务正式运行阶段测试是否有效。',
|
||
'Field("") or eval("") is detected in xpath, and the element containing these two expressions cannot be located normally during trial operation. Please test whether it is valid in the formal call stage.',
|
||
"warning"
|
||
);
|
||
return null;
|
||
}
|
||
let notify = false;
|
||
while (true) {
|
||
// console.log("handles");
|
||
try {
|
||
let h = order[len - 1];
|
||
console.log("current_handle", current_handle);
|
||
if (h != null && handles.includes(h)) {
|
||
await driver.switchTo().window(h);
|
||
current_handle = h;
|
||
console.log("switch to handle: ", h);
|
||
}
|
||
element = await findElement(driver, By.xpath, xpath, iframe);
|
||
break;
|
||
} catch (error) {
|
||
console.log("len", len);
|
||
len = len - 1;
|
||
if (!notify) {
|
||
notify = true;
|
||
// notify_browser("正在尝试在其他窗口中查找元素,请耐心等待。", "Trying to find elements in other windows, please wait patiently.", "info");
|
||
}
|
||
if (len == 0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (element == null && notifyBrowser) {
|
||
notify_browser(
|
||
"无法找到元素,请检查XPath是否正确:" + xpath,
|
||
"Cannot find the element, please check if the XPath is correct: " + xpath,
|
||
"warning"
|
||
);
|
||
}
|
||
if (element != null && scrollIntoView) {
|
||
// 浏览器切换到元素位置稍微靠上的位置
|
||
try {
|
||
// let script = `arguments[0].scrollIntoView(true);`;
|
||
let script = `arguments[0].scrollIntoView({block: "center", inline: "center"});`;
|
||
await driver.executeScript(script, element);
|
||
} catch (e) {
|
||
console.log("Cannot scrollIntoView");
|
||
}
|
||
}
|
||
return element;
|
||
}
|
||
|
||
async function beginInvoke(msg, ws) {
|
||
if (msg.type == 1) {
|
||
if (msg.message.id != -1) {
|
||
let url = "";
|
||
if (language == "zh") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/FlowChart_CN.html?id=${msg.message.id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address;
|
||
} else if (language == "en") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/FlowChart.html?id=${msg.message.id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address;
|
||
}
|
||
console.log(url);
|
||
flowchart_window.loadURL(url, { extraHeaders: "pragma: no-cache\n" });
|
||
}
|
||
mainWindow.hide();
|
||
// Prints the currently focused window bounds.
|
||
// This method has to be called on macOS before changing the window's bounds, otherwise it will throw an error.
|
||
// It will prompt an accessibility permission request dialog, if needed.
|
||
if (process.platform != "linux" && process.platform != "darwin") {
|
||
// 非用户信息模式下,设置窗口位置
|
||
if (
|
||
config_context.user_data_folder == null ||
|
||
config_context.user_data_folder == undefined ||
|
||
config_context.user_data_folder == ""
|
||
) {
|
||
const { windowManager } = require("node-window-manager");
|
||
const window = windowManager.getActiveWindow();
|
||
console.log(window);
|
||
windowManager.requestAccessibility();
|
||
// Sets the active window's bounds.
|
||
let size = screen.getPrimaryDisplay().workAreaSize;
|
||
let width = parseInt(size.width);
|
||
let height = parseInt(size.height * 0.6);
|
||
window.setBounds({
|
||
x: 0,
|
||
y: size.height * 0.4,
|
||
height: height,
|
||
width: width,
|
||
});
|
||
}
|
||
}
|
||
flowchart_window.show();
|
||
// flowchart_window.openDevTools();
|
||
} else if (msg.type == 2) {
|
||
// 键盘输入事件
|
||
// const robot = require("@jitsi/robotjs");
|
||
let keyInfo = msg.message.keyboardStr;
|
||
let enter = false;
|
||
if (/<enter>/i.test(keyInfo)) {
|
||
keyInfo = keyInfo.replace(/<enter>/gi, "");
|
||
enter = true;
|
||
}
|
||
let element = await findElementAcrossAllWindows(
|
||
msg,
|
||
(notifyBrowser = true),
|
||
(scrollIntoView = false)
|
||
);
|
||
await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
|
||
if (enter) {
|
||
await element.sendKeys(Key.ENTER);
|
||
}
|
||
} else if (msg.type == 3) {
|
||
try {
|
||
if (msg.from == 0) {
|
||
socket_flowchart.send(msg.message.pipe); //直接把消息转接
|
||
let message = JSON.parse(msg.message.pipe);
|
||
let type = message.type;
|
||
console.log("FROM Browser: ", message);
|
||
if (type.indexOf("Click") >= 0 || type.indexOf("Move") >= 0) {
|
||
let element = await findElementAcrossAllWindows(
|
||
message,
|
||
(notifyBrowser = true),
|
||
(scrollIntoView = false)
|
||
);
|
||
if (type.indexOf("Click") >= 0) {
|
||
await click_element(element, type);
|
||
} else if (type.indexOf("Move") >= 0) {
|
||
await driver.actions().move({ origin: element }).perform();
|
||
}
|
||
}
|
||
} else {
|
||
send_message_to_browser(msg.message.pipe);
|
||
console.log("FROM Flowchart: ", JSON.parse(msg.message.pipe));
|
||
}
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
} else if (msg.type == 4) {
|
||
//标记元素和试运行功能
|
||
let node = JSON.parse(msg.message.node);
|
||
let type = msg.message.type;
|
||
if (type == 0) {
|
||
//标记元素
|
||
let option = node.option;
|
||
let parameters = node.parameters;
|
||
//下面是让浏览器自动滚动到元素位置
|
||
if (option == 2 || option == 4 || option == 6 || option == 7) {
|
||
let xpath = parameters.xpath;
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
if (parameters.useLoop && option != 4 && option != 6) {
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
xpath = parent_xpath + xpath;
|
||
}
|
||
if (xpath.includes("point(")) {
|
||
xpath = "//body";
|
||
}
|
||
let elementInfo = { iframe: parameters.iframe, xpath: xpath, id: -1 };
|
||
//用于跳转到元素位置
|
||
let element = await findElementAcrossAllWindows(elementInfo);
|
||
} else if (option == 3) {
|
||
let params = parameters.params; //所有的提取数据参数
|
||
let param = params[0];
|
||
let xpath = param.relativeXPath;
|
||
if (param.relative) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
xpath = parent_xpath + xpath;
|
||
}
|
||
let elementInfo = { iframe: param.iframe, xpath: xpath, id: -1 };
|
||
let element = await findElementAcrossAllWindows(elementInfo);
|
||
} else if (option == 11) {
|
||
let params = parameters.params; //所有的提取数据参数
|
||
let i = parameters.index;
|
||
let param = params[i];
|
||
let xpath = param.relativeXPath;
|
||
if (param.relative) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
xpath = parent_xpath + xpath;
|
||
}
|
||
let elementInfo = { iframe: param.iframe, xpath: xpath, id: -1 };
|
||
let element = await findElementAcrossAllWindows(elementInfo);
|
||
} else if (option == 8) {
|
||
let loopType = parameters.loopType;
|
||
if (loopType <= 2) {
|
||
let xpath = "";
|
||
if (loopType <= 1) {
|
||
xpath = parameters.xpath;
|
||
} else if (loopType == 2) {
|
||
xpath = parameters.pathList.split("\n")[0].trim();
|
||
}
|
||
let elementInfo = { iframe: parameters.iframe, xpath: xpath, id: -1 };
|
||
let element = await findElementAcrossAllWindows(elementInfo);
|
||
} else if (loopType == 5) {
|
||
//JavaScript命令返回值
|
||
let code = parameters.code;
|
||
let waitTime = parameters.waitTime;
|
||
let element = await driver.findElement(By.tagName("body"));
|
||
let outcome = await execute_js(code, element, waitTime);
|
||
if (!outcome || outcome == -1) {
|
||
notify_browser(
|
||
"目前页面中,设置的循环“" +
|
||
node.title +
|
||
"”的JavaScript条件不成立",
|
||
"The condition of the loop " +
|
||
node.title +
|
||
" is not met, skip this loop.",
|
||
"warning"
|
||
);
|
||
} else {
|
||
notify_browser(
|
||
"目前页面中,设置的循环“" + node.title + "”的JavaScript条件成立",
|
||
"The condition of the loop " +
|
||
node.title +
|
||
" is met, continue this loop.",
|
||
"success"
|
||
);
|
||
}
|
||
}
|
||
} else if (option == 10) {
|
||
//条件分支
|
||
let condition = parameters.class; //条件类型
|
||
let result = -1;
|
||
let additionalInfo = "";
|
||
if (condition == 5 || condition == 7) {
|
||
//JavaScript命令返回值
|
||
let code = parameters.code;
|
||
let waitTime = parameters.waitTime;
|
||
let element = await driver.findElement(By.tagName("body"));
|
||
if (condition == 7) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
let elementInfo = {
|
||
iframe: parent_node.parameters.iframe,
|
||
xpath: parent_xpath,
|
||
id: -1,
|
||
};
|
||
element = await findElementAcrossAllWindows(elementInfo);
|
||
}
|
||
let outcome = await execute_js(code, element, waitTime);
|
||
if (!outcome) {
|
||
msg.message.result = 0; //条件不成立传入扩展
|
||
} else if (outcome == -1) {
|
||
msg.message.result = -1; //JS执行出错
|
||
} else {
|
||
msg.message.result = 1; //条件成立传入扩展
|
||
}
|
||
}
|
||
}
|
||
send_message_to_browser(JSON.stringify({ type: "trial", message: msg }));
|
||
} else {
|
||
//试运行
|
||
try {
|
||
let flowchart_url = flowchart_window.webContents.getURL();
|
||
} catch {
|
||
flowchart_window = null;
|
||
}
|
||
if (flowchart_window == null) {
|
||
notify_flowchart(
|
||
"试运行功能只能在任务设计阶段,Chrome浏览器打开时使用!",
|
||
"The trial run function can only be used when designing tasks and opening in Chrome browser!",
|
||
"error"
|
||
);
|
||
} else {
|
||
notify_browser(
|
||
"正在试运行操作:" + node.title,
|
||
"Trying to run the operation: " + node.title,
|
||
"info"
|
||
);
|
||
let option = node.option;
|
||
let parameters = node.parameters;
|
||
let beforeJS = "";
|
||
let beforeJSWaitTime = 0;
|
||
let afterJS = "";
|
||
let afterJSWaitTime = 0;
|
||
try {
|
||
beforeJS = parameters.beforeJS;
|
||
beforeJSWaitTime = parameters.beforeJSWaitTime;
|
||
afterJS = parameters.afterJS;
|
||
afterJSWaitTime = parameters.afterJSWaitTime;
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
if (option == 1) {
|
||
let url = parameters.links.split("\n")[0].trim();
|
||
if (parameters.useLoop) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
url = parent_node["parameters"]["textList"].split("\n")[0];
|
||
}
|
||
try {
|
||
await driver.get(url);
|
||
} catch (e) {
|
||
try {
|
||
await driver.switchTo().window(current_handle);
|
||
await driver.get(url);
|
||
} catch (e) {
|
||
let all_handles = await driver.getAllWindowHandles();
|
||
let handle = all_handles[all_handles.length - 1];
|
||
await driver.switchTo().window(handle);
|
||
await driver.get(url);
|
||
}
|
||
}
|
||
} else if (option == 2 || option == 7) {
|
||
//点击事件
|
||
let xpath = parameters.xpath;
|
||
let point = parameters.xpath;
|
||
if (xpath.includes("point(")) {
|
||
xpath = "//body";
|
||
}
|
||
let elementInfo = { iframe: parameters.iframe, xpath: xpath, id: -1 };
|
||
if (parameters.useLoop && !parameters.xpath.includes("point(")) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
elementInfo.xpath = parent_xpath + elementInfo.xpath;
|
||
}
|
||
let element = await findElementAcrossAllWindows(
|
||
elementInfo,
|
||
(notifyBrowser = false)
|
||
); //通过此函数找到元素并切换到对应的窗口
|
||
await execute_js(
|
||
parameters.beforeJS,
|
||
element,
|
||
parameters.beforeJSWaitTime
|
||
);
|
||
if (option == 2) {
|
||
if (parameters.xpath.includes("point(")) {
|
||
await click_element(element, point);
|
||
} else {
|
||
await click_element(element);
|
||
}
|
||
let alertHandleType = parameters.alertHandleType;
|
||
if (alertHandleType == 1) {
|
||
try {
|
||
await driver.switchTo().alert().accept();
|
||
} catch (e) {
|
||
console.log("No alert");
|
||
}
|
||
} else if (alertHandleType == 2) {
|
||
try {
|
||
await driver.switchTo().alert().dismiss();
|
||
} catch (e) {
|
||
console.log("No alert");
|
||
}
|
||
}
|
||
} else if (option == 7) {
|
||
await driver.actions().move({ origin: element }).perform();
|
||
}
|
||
await execute_js(
|
||
parameters.afterJS,
|
||
element,
|
||
parameters.afterJSWaitTime
|
||
);
|
||
send_message_to_browser(JSON.stringify({ type: "cancelSelection" }));
|
||
} else if (option == 3) {
|
||
//提取数据
|
||
notify_browser(
|
||
"提示:提取数据操作只能试运行设置的JavaScript语句,且只针对第一个匹配的元素。",
|
||
"Hint: can only test JavaScript statement set in the data extraction operation, and only for the first matching element.",
|
||
"info"
|
||
);
|
||
let params = parameters.params; //所有的提取数据参数
|
||
let not_found_xpaths = [];
|
||
for (let i = 0; i < params.length; i++) {
|
||
let param = params[i];
|
||
let xpath = param.relativeXPath;
|
||
if (param.relative) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
xpath = parent_xpath + xpath;
|
||
}
|
||
let elementInfo = { iframe: param.iframe, xpath: xpath, id: -1 };
|
||
let element = await findElementAcrossAllWindows(
|
||
elementInfo,
|
||
(notifyBrowser = false)
|
||
);
|
||
if (element != null) {
|
||
await execute_js(param.beforeJS, element, param.beforeJSWaitTime);
|
||
await execute_js(param.afterJS, element, param.afterJSWaitTime);
|
||
} else {
|
||
not_found_xpaths.push(xpath);
|
||
}
|
||
}
|
||
if (not_found_xpaths.length > 0) {
|
||
notify_browser(
|
||
"无法找到以下元素,请检查XPath是否正确:" +
|
||
not_found_xpaths.join("\n"),
|
||
"Cannot find the element, please check if the XPath is correct: " +
|
||
not_found_xpaths.join("\n"),
|
||
"warning"
|
||
);
|
||
}
|
||
} else if (option == 4) {
|
||
//键盘输入事件
|
||
let elementInfo = {
|
||
iframe: parameters.iframe,
|
||
xpath: parameters.xpath,
|
||
id: -1,
|
||
};
|
||
let value = node.parameters.value;
|
||
if (node.parameters.useLoop) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
value = parent_node["parameters"]["textList"].split("\n")[0];
|
||
let index = node.parameters.index;
|
||
if (index > 0) {
|
||
value = value.split("~")[index - 1];
|
||
}
|
||
}
|
||
let keyInfo = value;
|
||
let enter = false;
|
||
if (/<enter>/i.test(keyInfo)) {
|
||
keyInfo = keyInfo.replace(/<enter>/gi, "");
|
||
enter = true;
|
||
}
|
||
if (keyInfo.indexOf("Field(") >= 0 || keyInfo.indexOf("eval(") >= 0) {
|
||
//两秒后通知浏览器
|
||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||
notify_browser(
|
||
'检测到文字中包含Field("")或eval(""),试运行时无法输入两项表达式的替换值,请在任务正式运行阶段测试是否有效。',
|
||
'Field("") or eval("") is detected in the text, and the replacement value of the two expressions cannot be entered during trial operation. Please test whether it is valid in the formal call stage.',
|
||
"warning"
|
||
);
|
||
}
|
||
let element = await findElementAcrossAllWindows(
|
||
elementInfo,
|
||
(notifyBrowser = false)
|
||
);
|
||
await execute_js(beforeJS, element, beforeJSWaitTime);
|
||
await element.sendKeys(
|
||
Key.HOME,
|
||
Key.chord(Key.SHIFT, Key.END),
|
||
keyInfo
|
||
);
|
||
if (enter) {
|
||
await element.sendKeys(Key.ENTER);
|
||
}
|
||
await execute_js(afterJS, element, afterJSWaitTime);
|
||
} else if (option == 5) {
|
||
//自定义操作的JS代码
|
||
let code = parameters.code;
|
||
let codeMode = parameters.codeMode;
|
||
let waitTime = parameters.waitTime;
|
||
let element = await driver.findElement(By.tagName("body"));
|
||
if (codeMode == 0) {
|
||
await execute_js(code, element, waitTime);
|
||
} else if (codeMode == 8) {
|
||
//刷新页面
|
||
try {
|
||
await driver.navigate().refresh();
|
||
} catch (e) {
|
||
try {
|
||
await driver.switchTo().window(current_handle);
|
||
await driver.navigate().refresh();
|
||
} catch (e) {
|
||
let all_handles = await driver.getAllWindowHandles();
|
||
let handle = all_handles[all_handles.length - 1];
|
||
await driver.switchTo().window(handle);
|
||
await driver.navigate().refresh();
|
||
}
|
||
}
|
||
}
|
||
} else if (option == 6) {
|
||
//切换下拉选项
|
||
let optionMode = parseInt(parameters.optionMode);
|
||
let optionValue = parameters.optionValue;
|
||
if (node.parameters.useLoop) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
optionValue = parent_node["parameters"]["textList"].split("\n")[0];
|
||
let index = node.parameters.index;
|
||
if (index > 0) {
|
||
optionValue = optionValue.split("~")[index - 1];
|
||
}
|
||
}
|
||
let elementInfo = {
|
||
iframe: parameters.iframe,
|
||
xpath: parameters.xpath,
|
||
id: -1,
|
||
};
|
||
let element = await findElementAcrossAllWindows(
|
||
elementInfo,
|
||
(notifyBrowser = false)
|
||
);
|
||
execute_js(beforeJS, element, beforeJSWaitTime);
|
||
let dropdown = new Select(element);
|
||
// Interacting with dropdown element based on optionMode
|
||
switch (optionMode) {
|
||
case 0: //切换到下一个选项
|
||
let script = `var options = arguments[0].options;
|
||
for (var i = 0; i < options.length; i++) {
|
||
if (options[i].selected) {
|
||
options[i].selected = false;
|
||
if (i == options.length - 1) {
|
||
options[0].selected = true;
|
||
} else {
|
||
options[i + 1].selected = true;
|
||
}
|
||
break;
|
||
}
|
||
}`;
|
||
await driver.executeScript(script, element);
|
||
break;
|
||
case 1:
|
||
await dropdown.selectByIndex(parseInt(optionValue));
|
||
break;
|
||
case 2:
|
||
await dropdown.selectByValue(optionValue);
|
||
break;
|
||
case 3:
|
||
await dropdown.selectByVisibleText(optionValue);
|
||
break;
|
||
default:
|
||
throw new Error("Invalid option mode");
|
||
}
|
||
execute_js(afterJS, element, afterJSWaitTime);
|
||
} else if (option == 11) {
|
||
//单个提取数据参数
|
||
notify_browser(
|
||
"提示:提取数据操作只能试运行设置的JavaScript语句,且只针对第一个匹配的元素。",
|
||
"Hint: can only test JavaScript statement set in the data extraction operation, and only for the first matching element.",
|
||
"info"
|
||
);
|
||
let params = parameters.params; //所有的提取数据参数
|
||
let i = parameters.index;
|
||
let param = params[i];
|
||
let xpath = param.relativeXPath;
|
||
if (param.relative) {
|
||
let parent_node = JSON.parse(msg.message.parentNode);
|
||
let parent_xpath = parent_node.parameters.xpath;
|
||
if (parent_node.parameters.loopType == 2) {
|
||
parent_xpath = parent_node.parameters.pathList
|
||
.split("\n")[0]
|
||
.trim();
|
||
}
|
||
xpath = parent_xpath + xpath;
|
||
}
|
||
let elementInfo = { iframe: param.iframe, xpath: xpath, id: -1 };
|
||
let element = await findElementAcrossAllWindows(
|
||
elementInfo,
|
||
(notifyBrowser = false)
|
||
);
|
||
if (element != null) {
|
||
await execute_js(param.beforeJS, element, param.beforeJSWaitTime);
|
||
await execute_js(param.afterJS, element, param.afterJSWaitTime);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else if (msg.type == 5) {
|
||
let child = require("child_process").execFile;
|
||
// 参数顺序: 1. task id 2. server address 3. saved_file_name 4. "remote" or "local" 5. user_data_folder
|
||
// var parameters = [msg.message.id, server_address];
|
||
let parameters = [];
|
||
console.log(msg.message);
|
||
if (
|
||
msg.message.user_data_folder == null ||
|
||
msg.message.user_data_folder == undefined ||
|
||
msg.message.user_data_folder == ""
|
||
) {
|
||
parameters = [
|
||
"--ids",
|
||
"[" + msg.message.id + "]",
|
||
"--server_address",
|
||
server_address,
|
||
"--user_data",
|
||
0,
|
||
];
|
||
} else {
|
||
let user_data_folder_path = path.join(
|
||
task_server.getDir(),
|
||
msg.message.user_data_folder
|
||
);
|
||
parameters = [
|
||
"--ids",
|
||
"[" + msg.message.id + "]",
|
||
"--server_address",
|
||
server_address,
|
||
"--user_data",
|
||
1,
|
||
];
|
||
config.user_data_folder = msg.message.user_data_folder;
|
||
config.absolute_user_data_folder = user_data_folder_path;
|
||
fs.writeFileSync(
|
||
path.join(task_server.getDir(), "config.json"),
|
||
JSON.stringify(config)
|
||
);
|
||
}
|
||
if (msg.message.mysql_config_path != "-1") {
|
||
config.mysql_config_path = msg.message.mysql_config_path;
|
||
}
|
||
fs.writeFileSync(
|
||
path.join(task_server.getDir(), "config.json"),
|
||
JSON.stringify(config)
|
||
);
|
||
// child('Chrome/easyspider_executestage.exe', parameters, function(err,stdout, stderr) {
|
||
// console.log(stdout);
|
||
// });
|
||
|
||
let spawn = require("child_process").spawn;
|
||
if (
|
||
process.platform != "darwin" &&
|
||
msg.message.execute_type == 1 &&
|
||
msg.message.id != -1
|
||
) {
|
||
let child_process = spawn(execute_path, parameters);
|
||
child_process.stdout.on("data", function (data) {
|
||
console.log(data.toString());
|
||
});
|
||
}
|
||
ws.send(
|
||
JSON.stringify({
|
||
config_folder: task_server.getDir() + "/",
|
||
easyspider_location: task_server.getEasySpiderLocation(),
|
||
})
|
||
);
|
||
} else if (msg.type == 6) {
|
||
try {
|
||
flowchart_window.openDevTools();
|
||
} catch {
|
||
console.log("open devtools error");
|
||
}
|
||
try {
|
||
invoke_window.openDevTools();
|
||
} catch {
|
||
console.log("open devtools error");
|
||
}
|
||
} else if (msg.type == 7) {
|
||
// 获得当前页面Cookies
|
||
try {
|
||
let cookies = await driver.manage().getCookies();
|
||
console.log("Cookies: ", cookies);
|
||
let cookiesText = cookies
|
||
.map((cookie) => `${cookie.name}=${cookie.value}`)
|
||
.join("\n");
|
||
socket_flowchart.send(
|
||
JSON.stringify({ type: "GetCookies", message: cookiesText })
|
||
);
|
||
} catch {
|
||
console.log("Cannot get Cookies");
|
||
}
|
||
}
|
||
}
|
||
|
||
async function click_element(element, type = "click") {
|
||
try {
|
||
if (type == "loopClickEvery") {
|
||
await driver
|
||
.actions()
|
||
.keyDown(Key.CONTROL)
|
||
.click(element)
|
||
.keyUp(Key.CONTROL)
|
||
.perform();
|
||
} else if (type.includes("point(")) {
|
||
//point(10, 20)表示点击坐标为(10, 20)的位置
|
||
let point = type.substring(6, type.length - 1).split(",");
|
||
let x = parseInt(point[0]);
|
||
let y = parseInt(point[1]);
|
||
// let actions = driver.actions();
|
||
// await actions.move({origin: element}).perform();
|
||
// await actions.move({x: x, y: y}).perform();
|
||
// await actions.click().perform();
|
||
let script = `document.elementFromPoint(${x}, ${y}).click();`;
|
||
await driver.executeScript(script);
|
||
} else {
|
||
await element.click();
|
||
}
|
||
} catch (e) {
|
||
console.log(e);
|
||
await driver.executeScript("arguments[0].click();", element);
|
||
}
|
||
}
|
||
|
||
async function execute_js(js, element, wait_time = 3) {
|
||
let outcome = 0;
|
||
if (js.length != 0) {
|
||
try {
|
||
outcome = await driver.executeScript(js, element);
|
||
if (wait_time == 0) {
|
||
wait_time = 30000;
|
||
}
|
||
// await new Promise(resolve => setTimeout(resolve, wait_time));
|
||
} catch (e) {
|
||
// await new Promise(resolve => setTimeout(resolve, 2000));
|
||
notify_browser(
|
||
"执行JavaScript出错,请检查JavaScript语句是否正确:" +
|
||
js +
|
||
"\n错误信息:" +
|
||
e,
|
||
"Error executing JavaScript, please check if the JavaScript statement is correct: " +
|
||
js +
|
||
"\nError message: " +
|
||
e,
|
||
"error"
|
||
);
|
||
outcome = -1;
|
||
}
|
||
if (js.indexOf("Field(") >= 0 || js.indexOf("eval(") >= 0) {
|
||
//两秒后通知浏览器
|
||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||
notify_browser(
|
||
'检测到JavaScript中包含Field("")或eval(""),试运行时无法执行两项表达式,请在任务正式运行阶段测试是否有效。',
|
||
'Field("") or eval("") is detected in JavaScript, and the two expressions cannot be executed during trial operation. Please test whether it is valid in the formal call stage.',
|
||
"warning"
|
||
);
|
||
}
|
||
}
|
||
return outcome;
|
||
}
|
||
|
||
function notify_flowchart(msg_zh, msg_en, level = "info") {
|
||
socket_flowchart.send(
|
||
JSON.stringify({
|
||
type: "notify",
|
||
level: level,
|
||
msg_zh: msg_zh,
|
||
msg_en: msg_en,
|
||
})
|
||
);
|
||
}
|
||
|
||
function notify_browser(msg_zh, msg_en, level = "info") {
|
||
send_message_to_browser(
|
||
JSON.stringify({
|
||
type: "notify",
|
||
level: level,
|
||
msg_zh: msg_zh,
|
||
msg_en: msg_en,
|
||
})
|
||
);
|
||
}
|
||
|
||
function send_message_to_browser(message) {
|
||
socket_window.send(message);
|
||
for (let i in allWindowSockets) {
|
||
try {
|
||
allWindowSockets[i].send(message);
|
||
} catch {
|
||
console.log("Cannot send to socket with id: ", allWindowScoketNames[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
const WebSocket = require("ws");
|
||
const { all } = require("express/lib/application");
|
||
const { copy } = require("selenium-webdriver/io");
|
||
let wss = new WebSocket.Server({ port: websocket_port });
|
||
wss.on("connection", function (ws) {
|
||
ws.on("message", async function (message, isBinary) {
|
||
let msg = JSON.parse(message.toString());
|
||
// console.log("\n\nGET A MESSAGE: ", msg);
|
||
// console.log(msg, msg.type, msg.message);
|
||
if (msg.type == 0) {
|
||
if (msg.message.id == 0) {
|
||
socket_window = ws;
|
||
// socket_window.on('close', function (event) {
|
||
// socket_window = null;
|
||
// console.log("socket_window closed");
|
||
// });
|
||
// console.log("set socket_window at time: ", new Date());
|
||
} else if (msg.message.id == 1) {
|
||
socket_start = ws;
|
||
console.log("set socket_start at time: ", new Date());
|
||
} else if (msg.message.id == 2) {
|
||
socket_flowchart = ws;
|
||
// socket_flowchart.on('close', function (event) {
|
||
// socket_flowchart = null;
|
||
// console.log("socket_flowchart closed");
|
||
// });
|
||
console.log("set socket_flowchart at time: ", new Date());
|
||
} else {
|
||
//其他的ID是用来标识不同的浏览器标签页的
|
||
// await new Promise(resolve => setTimeout(resolve, 200));
|
||
let handles = await driver.getAllWindowHandles();
|
||
if (arrayDifference(handles, old_handles).length > 0) {
|
||
old_handles = handles;
|
||
current_handle = handles[handles.length - 1];
|
||
await driver.switchTo().window(current_handle);
|
||
console.log(
|
||
"New tab opened, change current_handle to: ",
|
||
current_handle
|
||
);
|
||
// 调整浏览器窗口大小,不然扩展会白屏
|
||
let size = await driver.manage().window().getRect();
|
||
let width = size.width;
|
||
let height = size.height;
|
||
await driver
|
||
.manage()
|
||
.window()
|
||
.setRect({ width: width, height: height + 10 });
|
||
// height = height - 1;
|
||
await driver
|
||
.manage()
|
||
.window()
|
||
.setRect({ width: width, height: height });
|
||
}
|
||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||
handle_pairs[msg.message.id] = current_handle;
|
||
console.log(
|
||
"Set handle_pair for id: ",
|
||
msg.message.id,
|
||
" to ",
|
||
current_handle,
|
||
", title is: ",
|
||
msg.message.title
|
||
);
|
||
socket_flowchart.send(
|
||
JSON.stringify({ type: "title", data: { title: msg.message.title } })
|
||
);
|
||
allWindowSockets.push(ws);
|
||
allWindowScoketNames.push(msg.message.id);
|
||
console.log(
|
||
"set socket for id: ",
|
||
msg.message.id,
|
||
" at time: ",
|
||
new Date()
|
||
);
|
||
ws.on("close", async function (event) {
|
||
let index = allWindowSockets.indexOf(ws);
|
||
if (index > -1) {
|
||
allWindowSockets.splice(index, 1);
|
||
allWindowScoketNames.splice(index, 1);
|
||
}
|
||
let handles = await driver.getAllWindowHandles();
|
||
if (handles.length < old_handles.length) {
|
||
old_handles = handles;
|
||
current_handle = handles[handles.length - 1];
|
||
await driver.switchTo().window(current_handle);
|
||
console.log(
|
||
"Current tab closed, change current_handle to: ",
|
||
current_handle
|
||
);
|
||
}
|
||
console.log(
|
||
"socket for id: ",
|
||
msg.message.id,
|
||
" closed at time: ",
|
||
new Date()
|
||
);
|
||
});
|
||
// console.log("handle_pairs: ", handle_pairs);
|
||
}
|
||
} else if (msg.type == 10) {
|
||
let leave_handle = handle_pairs[msg.message.id];
|
||
if (
|
||
leave_handle != null &&
|
||
leave_handle != undefined &&
|
||
leave_handle != ""
|
||
) {
|
||
await driver.switchTo().window(leave_handle);
|
||
console.log("Switch to handle: ", leave_handle);
|
||
current_handle = leave_handle;
|
||
}
|
||
} else {
|
||
await beginInvoke(msg, ws);
|
||
}
|
||
});
|
||
});
|
||
|
||
console.log(process.platform);
|
||
|
||
async function runBrowser(lang = "en", user_data_folder = "", mobile = false) {
|
||
const serviceBuilder = new ServiceBuilder(driverPath);
|
||
let options = new chrome.Options();
|
||
options.addArguments("--disable-blink-features=AutomationControlled");
|
||
options.addArguments("--disable-infobars");
|
||
// 添加实验性选项以排除'enable-automation'开关
|
||
options.set("excludeSwitches", ["enable-automation"]);
|
||
options.excludeSwitches("enable-automation");
|
||
|
||
// 添加实验性选项来禁用自动化扩展
|
||
options.set("useAutomationExtension", false);
|
||
// options.addArguments('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36');
|
||
options.set;
|
||
language = lang;
|
||
if (lang == "en") {
|
||
options.addExtensions(path.join(__dirname, "EasySpider_en.crx"));
|
||
} else if (lang == "zh") {
|
||
options.addExtensions(path.join(__dirname, "EasySpider_zh.crx"));
|
||
}
|
||
options.addExtensions(path.join(__dirname, "XPathHelper.crx"));
|
||
options.setChromeBinaryPath(chromeBinaryPath);
|
||
|
||
if (user_data_folder != "") {
|
||
let dir = path.join(task_server.getDir(), user_data_folder);
|
||
console.log(dir);
|
||
options.addArguments("--user-data-dir=" + dir);
|
||
config.user_data_folder = user_data_folder;
|
||
config_context.user_data_folder = user_data_folder;
|
||
fs.writeFileSync(
|
||
path.join(task_server.getDir(), "config.json"),
|
||
JSON.stringify(config)
|
||
);
|
||
} else {
|
||
config_context.user_data_folder = "";
|
||
}
|
||
if (mobile) {
|
||
const mobileEmulation = {
|
||
deviceName: "iPhone XR",
|
||
};
|
||
options.addArguments(
|
||
`--user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 13_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"`
|
||
);
|
||
options.setMobileEmulation(mobileEmulation);
|
||
}
|
||
driver = new Builder()
|
||
.forBrowser("chrome")
|
||
.setChromeOptions(options)
|
||
.setChromeService(serviceBuilder)
|
||
.build();
|
||
await driver
|
||
.manage()
|
||
.setTimeouts({ implicit: 3, pageLoad: 10000, script: 10000 });
|
||
await driver.executeScript(
|
||
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
||
);
|
||
// await driver.executeScript("localStorage.clear();"); //重置参数数量
|
||
const cdpConnection = await driver.createCDPConnection("page");
|
||
let stealth_path = path.join(__dirname, "stealth.min.js");
|
||
let stealth = fs.readFileSync(stealth_path, "utf8");
|
||
await cdpConnection.execute("Page.addScriptToEvaluateOnNewDocument", {
|
||
source: stealth,
|
||
});
|
||
if (config_context.user_data_folder == "") {
|
||
//调整浏览器窗口大小
|
||
let size = await driver.manage().window().getRect();
|
||
let width = size.width;
|
||
let height = size.height;
|
||
await driver
|
||
.manage()
|
||
.window()
|
||
.setRect({ width: width * 1.2, height: height });
|
||
}
|
||
try {
|
||
if (mobile) {
|
||
await driver.get(
|
||
server_address +
|
||
"/taskGrid/taskList.html?wsport=" +
|
||
websocket_port +
|
||
"&backEndAddressServiceWrapper=" +
|
||
server_address +
|
||
"&mobile=1&lang=" +
|
||
lang
|
||
);
|
||
} else {
|
||
await driver.get(
|
||
server_address +
|
||
"/taskGrid/taskList.html?wsport=" +
|
||
websocket_port +
|
||
"&backEndAddressServiceWrapper=" +
|
||
server_address +
|
||
"&lang=" +
|
||
lang
|
||
);
|
||
}
|
||
old_handles = await driver.getAllWindowHandles();
|
||
current_handle = old_handles[old_handles.length - 1];
|
||
} finally {
|
||
// await driver.quit(); // 退出浏览器
|
||
}
|
||
}
|
||
|
||
function handleOpenBrowser(
|
||
event,
|
||
lang = "en",
|
||
user_data_folder = "",
|
||
mobile = false
|
||
) {
|
||
const webContents = event.sender;
|
||
const win = BrowserWindow.fromWebContents(webContents);
|
||
runBrowser(lang, user_data_folder, mobile);
|
||
let size = screen.getPrimaryDisplay().workAreaSize;
|
||
let width = parseInt(size.width);
|
||
let height = parseInt(size.height * 0.5);
|
||
flowchart_window = new BrowserWindow({
|
||
x: 0,
|
||
y: 0,
|
||
width: width,
|
||
height: height,
|
||
icon: iconPath,
|
||
maximizable: true,
|
||
resizable: true,
|
||
});
|
||
let url = "";
|
||
let id = -1;
|
||
if (lang == "en") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/FlowChart.html?id=${id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address +
|
||
"&mobile=" +
|
||
mobile.toString();
|
||
} else if (lang == "zh") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/FlowChart_CN.html?id=${id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address +
|
||
"&mobile=" +
|
||
mobile.toString();
|
||
}
|
||
// and load the index.html of the app.
|
||
flowchart_window.loadURL(url, { extraHeaders: "pragma: no-cache\n" });
|
||
if (process.platform != "darwin") {
|
||
flowchart_window.hide();
|
||
}
|
||
flowchart_window.on("close", function (event) {
|
||
mainWindow.show();
|
||
driver.quit();
|
||
});
|
||
}
|
||
|
||
function handleOpenInvoke(event, lang = "en") {
|
||
invoke_window = new BrowserWindow({ icon: iconPath });
|
||
let url = "";
|
||
language = lang;
|
||
if (lang == "en") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/taskList.html?type=1&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address;
|
||
} else if (lang == "zh") {
|
||
url =
|
||
server_address +
|
||
`/taskGrid/taskList.html?type=1&wsport=${websocket_port}&backEndAddressServiceWrapper=` +
|
||
server_address +
|
||
"&lang=zh";
|
||
}
|
||
// and load the index.html of the app.
|
||
invoke_window.loadURL(url, { extraHeaders: "pragma: no-cache\n" });
|
||
invoke_window.maximize();
|
||
mainWindow.hide();
|
||
invoke_window.on("close", function (event) {
|
||
mainWindow.show();
|
||
});
|
||
}
|
||
|
||
// This method will be called when Electron has finished
|
||
// initialization and is ready to create browser windows.
|
||
// Some APIs can only be used after this event occurs.
|
||
app.whenReady().then(() => {
|
||
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||
details.requestHeaders["Accept-Language"] = "zh";
|
||
callback({ cancel: false, requestHeaders: details.requestHeaders });
|
||
});
|
||
ipcMain.on("start-design", handleOpenBrowser);
|
||
ipcMain.on("start-invoke", handleOpenInvoke);
|
||
ipcMain.on("accept-agreement", function (event, arg) {
|
||
config.copyright = 1;
|
||
fs.writeFileSync(
|
||
path.join(task_server.getDir(), "config.json"),
|
||
JSON.stringify(config)
|
||
);
|
||
});
|
||
createWindow();
|
||
|
||
app.on("activate", function () {
|
||
// On MacOS it's common to re-create a window in the app when the
|
||
// dock icon is clicked and there are no other windows open.
|
||
if (BrowserWindow.getAllWindows().length === 0) {
|
||
createWindow();
|
||
}
|
||
});
|
||
// 获取Chrome版本
|
||
execFile(chromeBinaryPath, ["--version"], (error, chromeStdout) => {
|
||
if (error) {
|
||
dialog.showErrorBox("Chrome Error", "Failed to retrieve Chrome version.");
|
||
return;
|
||
}
|
||
|
||
// Chrome的版本输出通常是 "Google Chrome xx.x.xxx.x"
|
||
const chromeVersionMatch = chromeStdout.match(/\d+/);
|
||
if (!chromeVersionMatch) {
|
||
dialog.showErrorBox("Chrome Error", "Unable to parse Chrome version.");
|
||
return;
|
||
} else {
|
||
console.log("\n\n\nChrome version: ", chromeVersionMatch[0]);
|
||
}
|
||
const chromeVersion = chromeVersionMatch[0];
|
||
|
||
// 获取ChromeDriver版本
|
||
execFile(driverPath, ["--version"], (error, chromedriverStdout) => {
|
||
if (error) {
|
||
dialog.showErrorBox(
|
||
"ChromeDriver Error",
|
||
"Failed to retrieve ChromeDriver version."
|
||
);
|
||
return;
|
||
} else {
|
||
console.log("\n\n\nChromeDriver version: ", chromedriverStdout);
|
||
}
|
||
|
||
// ChromeDriver的版本输出通常是 "ChromeDriver xx.x.xxx.x (更多信息)"
|
||
const chromedriverVersionMatch =
|
||
chromedriverStdout.match(/\d+\.\d+\.\d+\.\d+/);
|
||
if (!chromedriverVersionMatch) {
|
||
dialog.showErrorBox(
|
||
"ChromeDriver Error",
|
||
"Unable to parse ChromeDriver version."
|
||
);
|
||
return;
|
||
}
|
||
const chromedriverVersion = chromedriverVersionMatch[0];
|
||
|
||
// 检查主版本号是否匹配
|
||
if (chromeVersion.split(".")[0] !== chromedriverVersion.split(".")[0]) {
|
||
dialog.showErrorBox(
|
||
"ChromeDriver版本不匹配|ChromeDriver Version Mismatch",
|
||
` Chrome的版本(${chromeVersion})与ChromeDriver的版本(${chromedriverVersion})不匹配,软件将无法正常使用。请阅读文件夹下的“浏览器闪退解决方案.txt”升级ChromeDriver到${chromeVersion}.*版本以正常使用软件。\n\nThe version of Chrome (${chromeVersion}) does not match the version of ChromeDriver (${chromedriverVersion}), this will cause the software to be unable to run normally. Please read the "If Chrome exits when desigining a task.txt" in the folder to upgrade ChromeDriver to ${chromeVersion}.* version to use the software normally.`
|
||
);
|
||
} else {
|
||
// 版本匹配,继续应用流程
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// Quit when all windows are closed, except on macOS. There, it's common
|
||
// for applications and their menu bar to stay active until the user quits
|
||
// explicitly with Cmd + Q.
|
||
app.on("window-all-closed", function () {
|
||
if (process.platform !== "darwin") {
|
||
app.quit();
|
||
}
|
||
});
|
||
|
||
// In this file you can include the rest of your app's specific main process
|
||
// code. You can also put them in separate files and require them here.
|
||
|
||
function arrayDifference(arr1, arr2) {
|
||
return arr1.filter((item) => !arr2.includes(item));
|
||
}
|