main.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. // Modules to control application life and create native browser window
  2. const {app, BrowserWindow, dialog, ipcMain, screen, session} = require('electron');
  3. app.commandLine.appendSwitch("--disable-http-cache");
  4. const {Builder, By, Key, until, Select, StaleElementReferenceException} = require("selenium-webdriver");
  5. const chrome = require('selenium-webdriver/chrome');
  6. const {ServiceBuilder} = require('selenium-webdriver/chrome');
  7. const {rootCertificates} = require('tls');
  8. const {exit} = require('process');
  9. const path = require('path');
  10. const fs = require('fs');
  11. const {exec, spawn} = require('child_process');
  12. const iconPath = path.join(__dirname, 'favicon.ico');
  13. const task_server = require(path.join(__dirname, 'server.js'));
  14. const util = require('util');
  15. let config = fs.readFileSync(path.join(task_server.getDir(), `config.json`), 'utf8');
  16. config = JSON.parse(config);
  17. if (config.debug) {
  18. let logPath = 'info.log'
  19. let logFile = fs.createWriteStream(logPath, {flags: 'a'})
  20. console.log = function () {
  21. logFile.write(util.format.apply(null, arguments) + '\n')
  22. process.stdout.write(util.format.apply(null, arguments) + '\n')
  23. }
  24. console.error = function () {
  25. logFile.write(util.format.apply(null, arguments) + '\n')
  26. process.stderr.write(util.format.apply(null, arguments) + '\n')
  27. }
  28. }
  29. let allWindowSockets = [];
  30. let allWindowScoketNames = [];
  31. task_server.start(config.webserver_port); //start local server
  32. let server_address = `${config.webserver_address}:${config.webserver_port}`;
  33. const websocket_port = 8084; //目前只支持8084端口,写死,因为扩展里面写死了
  34. console.log("server_address: " + server_address);
  35. let driverPath = "";
  36. let chromeBinaryPath = "";
  37. let execute_path = "";
  38. console.log(process.arch);
  39. exec(`wmic os get Caption`, function (error, stdout, stderr) {
  40. if (error) {
  41. console.error(`执行的错误: ${error}`);
  42. return;
  43. }
  44. if (stdout.includes('Windows 7')) {
  45. console.log('Windows 7');
  46. let sys_arch = config.sys_arch;
  47. if (sys_arch === 'x64') {
  48. dialog.showMessageBoxSync({
  49. type: 'error',
  50. title: 'Error',
  51. 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.',
  52. });
  53. }
  54. } else {
  55. console.log('Not Windows 7');
  56. }
  57. });
  58. if (process.platform === 'win32' && process.arch === 'ia32') {
  59. driverPath = path.join(__dirname, "chrome_win32/chromedriver_win32.exe");
  60. chromeBinaryPath = path.join(__dirname, "chrome_win32/chrome.exe");
  61. execute_path = path.join(__dirname, "chrome_win32/execute.bat");
  62. } else if (process.platform === 'win32' && process.arch === 'x64') {
  63. driverPath = path.join(__dirname, "chrome_win64/chromedriver_win64.exe");
  64. chromeBinaryPath = path.join(__dirname, "chrome_win64/chrome.exe");
  65. execute_path = path.join(__dirname, "chrome_win64/execute.bat");
  66. } else if (process.platform === 'darwin') {
  67. driverPath = path.join(__dirname, "chromedriver_mac64");
  68. chromeBinaryPath = path.join(__dirname, "chrome_mac64.app/Contents/MacOS/Google Chrome");
  69. execute_path = path.join(__dirname, "");
  70. } else if (process.platform === 'linux') {
  71. driverPath = path.join(__dirname, "chrome_linux64/chromedriver_linux64");
  72. chromeBinaryPath = path.join(__dirname, "chrome_linux64/chrome");
  73. execute_path = path.join(__dirname, "chrome_linux64/execute.sh");
  74. }
  75. console.log(driverPath, chromeBinaryPath, execute_path);
  76. let language = "en";
  77. let driver = null;
  78. let mainWindow = null;
  79. let flowchart_window = null;
  80. let current_handle = null;
  81. let old_handles = [];
  82. let handle_pairs = {};
  83. let socket_window = null;
  84. let socket_start = null;
  85. let socket_flowchart = null;
  86. let invoke_window = null;
  87. // var ffi = require('ffi-napi');
  88. // var libm = ffi.Library('libm', {
  89. // 'ceil': [ 'double', [ 'double' ] ]
  90. // });
  91. // libm.ceil(1.5); // 2
  92. // const {user32FindWindowEx,
  93. // winspoolGetDefaultPrinter,} = require('win32-api/fun');
  94. // async function testt(){
  95. // // 获取当前电脑当前用户默认打印机名
  96. // const printerName = await winspoolGetDefaultPrinter()
  97. // console.log(printerName);
  98. // }
  99. // testt();
  100. function createWindow() {
  101. // Create the browser window.
  102. mainWindow = new BrowserWindow({
  103. width: 550,
  104. height: 750,
  105. webPreferences: {
  106. preload: path.join(__dirname, 'src/js/preload.js')
  107. },
  108. icon: iconPath,
  109. // frame: false, //取消window自带的关闭最小化等
  110. resizable: false //禁止改变主窗口尺寸
  111. })
  112. // and load the index.html of the app.
  113. // mainWindow.loadFile('src/index.html');
  114. mainWindow.loadURL(server_address + '/index.html?user_data_folder=' + config.user_data_folder + "&copyright=" + config.copyright, {extraHeaders: 'pragma: no-cache\n'});
  115. // 隐藏菜单栏
  116. const {Menu} = require('electron');
  117. Menu.setApplicationMenu(null);
  118. mainWindow.on('close', function (e) {
  119. if (process.platform !== 'darwin') {
  120. app.quit();
  121. }
  122. });
  123. // mainWindow.webContents.openDevTools();
  124. // Open the DevTools.
  125. // mainWindow.webContents.openDevTools()
  126. }
  127. async function findElementRecursive(driver, by, value, frames) {
  128. for (const frame of frames) {
  129. try {
  130. // Try to switch to the frame
  131. try {
  132. await driver.switchTo().frame(frame);
  133. } catch (error) {
  134. if (error.name.indexOf('StaleElement') >= 0) {
  135. // If the frame is stale, switch to the parent frame and then retry switching to the frame
  136. await driver.switchTo().parentFrame();
  137. await driver.switchTo().frame(frame);
  138. } else {
  139. // If it is another exception rethrow it
  140. throw error;
  141. }
  142. }
  143. let element;
  144. try {
  145. // Attempt to find the element in this frame
  146. element = await driver.findElement(by(value));
  147. return element;
  148. } catch (error) {
  149. if (error.name.indexOf('NoSuchElement') >= 0) {
  150. // The element was not found in this frame, recurse into nested iframes
  151. const nestedFrames = await driver.findElements(By.tagName("iframe"));
  152. if (nestedFrames.length > 0) {
  153. element = await findElementRecursive(driver, by, value, nestedFrames);
  154. if (element) {
  155. return element;
  156. }
  157. }
  158. } else {
  159. // If it is another exception, log it
  160. console.error(`Exception while processing frame: ${error}`);
  161. }
  162. }
  163. } catch (error) {
  164. console.error(`Exception while processing frame: ${error}`);
  165. }
  166. }
  167. throw new Error(`Element ${value} not found in any frame or iframe`);
  168. }
  169. async function findElement(driver, by, value, iframe = false) {
  170. // Switch back to the main document
  171. await driver.switchTo().defaultContent();
  172. if (iframe) {
  173. const frames = await driver.findElements(By.tagName("iframe"));
  174. if (frames.length === 0) {
  175. throw new Error(`No iframes found in the current page while searching for ${value}`);
  176. }
  177. const element = await findElementRecursive(driver, by, value, frames);
  178. return element;
  179. } else {
  180. // Find element in the main document as normal
  181. let element = await driver.findElement(by(value));
  182. return element;
  183. }
  184. }
  185. async function findElementAcrossAllWindows(msg, notifyBrowser=true) {
  186. let handles = await driver.getAllWindowHandles();
  187. // console.log("handles", handles);
  188. let content_handle = current_handle;
  189. let id = -1;
  190. try {
  191. id = msg.message.id;
  192. } catch {
  193. id = msg.id;
  194. }
  195. if (id == -1) { //如果是-1,从当前窗口开始搜索
  196. content_handle = current_handle;
  197. } else {
  198. content_handle = handle_pairs[id];
  199. }
  200. // console.log(msg.message.id, content_handle);
  201. let order = [...handles.filter(handle => handle != current_handle && handle != content_handle), current_handle, content_handle]; //搜索顺序
  202. let len = order.length;
  203. let element = null;
  204. let iframe = false;
  205. try {
  206. iframe = msg.message.iframe;
  207. } catch {
  208. iframe = msg.iframe;
  209. }
  210. // if (iframe) {
  211. // notify_browser("在IFrame中执行操作可能需要较长时间,请耐心等待。", "Executing operations in IFrame may take a long time, please wait patiently.", "info");
  212. // }
  213. let xpath = "";
  214. try {
  215. xpath = msg.message.xpath;
  216. } catch {
  217. xpath = msg.xpath;
  218. }
  219. let notify = false;
  220. while (true) {
  221. // console.log("handles");
  222. try {
  223. let h = order[len - 1];
  224. console.log("current_handle", current_handle);
  225. if (h != null && handles.includes(h)) {
  226. await driver.switchTo().window(h);
  227. current_handle = h;
  228. console.log("switch to handle: ", h);
  229. }
  230. element = await findElement(driver, By.xpath, xpath, iframe);
  231. break;
  232. } catch (error) {
  233. console.log("len", len);
  234. len = len - 1;
  235. if (!notify) {
  236. notify = true;
  237. notify_browser("正在尝试在其他窗口中查找元素,请耐心等待。", "Trying to find elements in other windows, please wait patiently.", "info");
  238. }
  239. if (len == 0) {
  240. break;
  241. }
  242. }
  243. }
  244. if (element == null && notifyBrowser) {
  245. notify_browser("无法找到元素,请检查xpath是否正确:" + xpath, "Cannot find the element, please check if the xpath is correct: " + xpath, "warning");
  246. }
  247. return element;
  248. }
  249. async function beginInvoke(msg, ws) {
  250. if (msg.type == 1) {
  251. if (msg.message.id != -1) {
  252. let url = "";
  253. if (language == "zh") {
  254. url = server_address + `/taskGrid/FlowChart_CN.html?id=${msg.message.id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address;
  255. } else if (language == "en") {
  256. url = server_address + `/taskGrid/FlowChart.html?id=${msg.message.id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address;
  257. }
  258. console.log(url);
  259. flowchart_window.loadURL(url, {extraHeaders: 'pragma: no-cache\n'});
  260. }
  261. mainWindow.hide();
  262. // Prints the currently focused window bounds.
  263. // This method has to be called on macOS before changing the window's bounds, otherwise it will throw an error.
  264. // It will prompt an accessibility permission request dialog, if needed.
  265. if (process.platform != "linux" && process.platform != "darwin") {
  266. const {windowManager} = require("node-window-manager");
  267. const window = windowManager.getActiveWindow();
  268. console.log(window);
  269. windowManager.requestAccessibility();
  270. // Sets the active window's bounds.
  271. let size = screen.getPrimaryDisplay().workAreaSize
  272. let width = parseInt(size.width)
  273. let height = parseInt(size.height * 0.6)
  274. window.setBounds({x: 0, y: size.height * 0.4, height: height, width: width});
  275. }
  276. flowchart_window.show();
  277. // flowchart_window.openDevTools();
  278. } else if (msg.type == 2) {
  279. // 键盘输入事件
  280. // const robot = require("@jitsi/robotjs");
  281. let keyInfo = msg.message.keyboardStr;
  282. let enter = false;
  283. if (/<enter>/i.test(keyInfo)) {
  284. keyInfo = keyInfo.replace(/<enter>/gi, '');
  285. enter = true;
  286. }
  287. let element = await findElementAcrossAllWindows(msg);
  288. await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
  289. if (enter) {
  290. await element.sendKeys(Key.ENTER);
  291. }
  292. } else if (msg.type == 3) {
  293. try {
  294. if (msg.from == 0) {
  295. socket_flowchart.send(msg.message.pipe); //直接把消息转接
  296. let message = JSON.parse(msg.message.pipe);
  297. let type = message.type;
  298. console.log("FROM Browser: ", message);
  299. if (type.indexOf("Click") >= 0 || type.indexOf("Move") >= 0) {
  300. let element = await findElementAcrossAllWindows(message);
  301. if (type.indexOf("Click") >= 0) {
  302. await click_element(element);
  303. } else if (type.indexOf("Move") >= 0) {
  304. await driver.actions().move({origin: element}).perform();
  305. }
  306. }
  307. } else {
  308. send_message_to_browser(msg.message.pipe);
  309. console.log("FROM Flowchart: ", JSON.parse(msg.message.pipe));
  310. }
  311. } catch (e) {
  312. console.log(e);
  313. }
  314. } else if (msg.type == 4) { //试运行功能
  315. if (socket_window == null) {
  316. notify_flowchart("试运行功能只能在设计任务阶段,Chrome浏览器打开时使用!", "The trial run function can only be used when designing tasks and opening in Chrome browser!", "error");
  317. } else {
  318. let node = JSON.parse(msg.message.node);
  319. notify_browser("正在试运行操作:" + node.title, "Trying to run the operation: " + node.title, "info");
  320. let option = node.option;
  321. let parameters = node.parameters;
  322. let beforeJS = "";
  323. let beforeJSWaitTime = 0;
  324. let afterJS = "";
  325. let afterJSWaitTime = 0;
  326. try {
  327. beforeJS = parameters.beforeJS;
  328. beforeJSWaitTime = parameters.beforeJSWaitTime;
  329. afterJS = parameters.afterJS;
  330. afterJSWaitTime = parameters.afterJSWaitTime;
  331. } catch (e) {
  332. console.log(e);
  333. }
  334. if (option == 1) {
  335. let url = parameters.links.split("\n")[0].trim();
  336. if (parameters.useLoop) {
  337. let parent_node = JSON.parse(msg.message.parentNode);
  338. url = parent_node["parameters"]["textList"].split("\n")[0];
  339. }
  340. await driver.get(url);
  341. }
  342. else if (option == 2 || option == 7) { //点击事件
  343. let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
  344. if (parameters.useLoop) {
  345. let parent_node = JSON.parse(msg.message.parentNode);
  346. let parent_xpath = parent_node.parameters.xpath;
  347. elementInfo.xpath = parent_xpath + elementInfo.xpath;
  348. }
  349. let element = await findElementAcrossAllWindows(elementInfo);
  350. if (beforeJS != "") {
  351. await driver.executeScript(beforeJS, element);
  352. await new Promise(resolve => setTimeout(resolve, beforeJSWaitTime));
  353. }
  354. if (option == 2) {
  355. await click_element(element);
  356. } else if (option == 7) {
  357. await driver.actions().move({origin: element}).perform();
  358. }
  359. if (afterJS != "") {
  360. await driver.executeScript(afterJS, element);
  361. await new Promise(resolve => setTimeout(resolve, afterJSWaitTime));
  362. }
  363. send_message_to_browser(JSON.stringify({"type": "cancelSelection"}));
  364. }
  365. else if (option == 3) { //提取数据
  366. notify_browser("提示:提取数据操作只能试运行设置的JavaScript语句。", "Hint: can only test JavaScript statement set in the data extraction operation.", "info");
  367. let paras = parameters.paras; //所有的提取数据参数
  368. let not_found_xpaths = [];
  369. for (let i = 0; i < paras.length; i++) {
  370. let para = paras[i];
  371. let xpath = para.relativeXPath;
  372. if (para.relative) {
  373. let parent_node = JSON.parse(msg.message.parentNode);
  374. let parent_xpath = parent_node.parameters.xpath;
  375. xpath = parent_xpath + xpath;
  376. }
  377. let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
  378. let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser=false);
  379. if (element != null) {
  380. if (para.beforeJS != "") {
  381. await driver.executeScript(para.beforeJS, element);
  382. await new Promise(resolve => setTimeout(resolve, para.beforeJSWaitTime));
  383. }
  384. if (para.afterJS != "") {
  385. await driver.executeScript(para.afterJS, element);
  386. await new Promise(resolve => setTimeout(resolve, para.afterJSWaitTime));
  387. }
  388. } else {
  389. not_found_xpaths.push(xpath);
  390. }
  391. }
  392. if (not_found_xpaths.length > 0) {
  393. 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");
  394. }
  395. }
  396. else if (option == 4) { //键盘输入事件
  397. let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
  398. let value = node.parameters.value;
  399. if (node.parameters.useLoop) {
  400. let parent_node = JSON.parse(msg.message.parentNode);
  401. value = parent_node["parameters"]["textList"].split("\n")[0];
  402. let index = node.parameters.index;
  403. if (index > 0) {
  404. value = value.split("~")[index - 1];
  405. }
  406. }
  407. let keyInfo = value
  408. let enter = false;
  409. if (/<enter>/i.test(keyInfo)) {
  410. keyInfo = keyInfo.replace(/<enter>/gi, '');
  411. enter = true;
  412. }
  413. let element = await findElementAcrossAllWindows(elementInfo);
  414. if (beforeJS != "") {
  415. await driver.executeScript(beforeJS, element);
  416. await new Promise(resolve => setTimeout(resolve, beforeJSWaitTime));
  417. }
  418. await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
  419. if (enter) {
  420. await element.sendKeys(Key.ENTER);
  421. }
  422. if (afterJS != "") {
  423. await driver.executeScript(afterJS, element);
  424. await new Promise(resolve => setTimeout(resolve, afterJSWaitTime));
  425. }
  426. } else if (option == 5) { //自定义操作的JS代码
  427. let code = parameters.code;
  428. await driver.executeScript(code);
  429. } else if (option == 6) { //切换下拉选项
  430. let optionMode = parseInt(parameters.optionMode);
  431. let optionValue = parameters.optionValue;
  432. if (node.parameters.useLoop) {
  433. let parent_node = JSON.parse(msg.message.parentNode);
  434. optionValue = parent_node["parameters"]["textList"].split("\n")[0];
  435. let index = node.parameters.index;
  436. if (index > 0) {
  437. optionValue = optionValue.split("~")[index - 1];
  438. }
  439. }
  440. let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
  441. let element = await findElementAcrossAllWindows(elementInfo);
  442. if (beforeJS != "") {
  443. await driver.executeScript(beforeJS, element);
  444. await new Promise(resolve => setTimeout(resolve, beforeJSWaitTime));
  445. }
  446. let dropdown = new Select(element);
  447. // Interacting with dropdown element based on optionMode
  448. switch (optionMode) {
  449. case 0: //切换到下一个选项
  450. let script = `var options = arguments[0].options;
  451. for (var i = 0; i < options.length; i++) {
  452. if (options[i].selected) {
  453. options[i].selected = false;
  454. if (i == options.length - 1) {
  455. options[0].selected = true;
  456. } else {
  457. options[i + 1].selected = true;
  458. }
  459. break;
  460. }
  461. }`;
  462. await driver.executeScript(script, element);
  463. break;
  464. case 1:
  465. await dropdown.selectByIndex(parseInt(optionValue));
  466. break;
  467. case 2:
  468. await dropdown.selectByValue(optionValue);
  469. break;
  470. case 3:
  471. await dropdown.selectByVisibleText(optionValue);
  472. break;
  473. default:
  474. throw new Error('Invalid option mode');
  475. }
  476. }
  477. }
  478. } else if (msg.type == 8) { //展示元素功能
  479. } else if (msg.type == 5) {
  480. let child = require('child_process').execFile;
  481. // 参数顺序: 1. task id 2. server address 3. saved_file_name 4. "remote" or "local" 5. user_data_folder
  482. // var parameters = [msg.message.id, server_address];
  483. let parameters = [];
  484. console.log(msg.message)
  485. if (msg.message.user_data_folder == null || msg.message.user_data_folder == undefined || msg.message.user_data_folder == "") {
  486. parameters = ["--ids", "[" + msg.message.id + "]", "--server_address", server_address, "--user_data", 0];
  487. } else {
  488. let user_data_folder_path = path.join(task_server.getDir(), msg.message.user_data_folder);
  489. parameters = ["--ids", "[" + msg.message.id + "]", "--server_address", server_address, "--user_data", 1];
  490. config.user_data_folder = msg.message.user_data_folder;
  491. config.absolute_user_data_folder = user_data_folder_path;
  492. fs.writeFileSync(path.join(task_server.getDir(), "config.json"), JSON.stringify(config));
  493. }
  494. if (msg.message.mysql_config_path != "-1") {
  495. config.mysql_config_path = msg.message.mysql_config_path;
  496. }
  497. fs.writeFileSync(path.join(task_server.getDir(), "config.json"), JSON.stringify(config));
  498. // child('Chrome/easyspider_executestage.exe', parameters, function(err,stdout, stderr) {
  499. // console.log(stdout);
  500. // });
  501. let spawn = require("child_process").spawn;
  502. if (process.platform != "darwin" && msg.message.execute_type == 1 && msg.message.id != -1) {
  503. let child_process = spawn(execute_path, parameters);
  504. child_process.stdout.on('data', function (data) {
  505. console.log(data.toString());
  506. });
  507. }
  508. ws.send(JSON.stringify({
  509. "config_folder": task_server.getDir() + "/",
  510. "easyspider_location": task_server.getEasySpiderLocation()
  511. }));
  512. } else if (msg.type == 6) {
  513. try {
  514. flowchart_window.openDevTools();
  515. } catch {
  516. console.log("open devtools error");
  517. }
  518. try {
  519. invoke_window.openDevTools();
  520. } catch {
  521. console.log("open devtools error");
  522. }
  523. } else if (msg.type == 7) {
  524. // 获得当前页面Cookies
  525. try {
  526. let cookies = await driver.manage().getCookies();
  527. console.log("Cookies: ", cookies);
  528. let cookiesText = cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('\n');
  529. socket_flowchart.send(JSON.stringify({"type": "GetCookies", "message": cookiesText}));
  530. } catch {
  531. console.log("Cannot get Cookies");
  532. }
  533. }
  534. }
  535. async function click_element(element) {
  536. try {
  537. await element.click();
  538. } catch (e) {
  539. console.log(e);
  540. await driver.executeScript("arguments[0].click();", element);
  541. }
  542. }
  543. function notify_flowchart(msg_zh, msg_en, level = "info") {
  544. socket_flowchart.send(JSON.stringify({"type": "notify", "level": level, "msg_zh": msg_zh, "msg_en": msg_en}));
  545. }
  546. function notify_browser(msg_zh, msg_en, level = "info") {
  547. send_message_to_browser(JSON.stringify({"type": "notify", "level": level, "msg_zh": msg_zh, "msg_en": msg_en}));
  548. }
  549. function send_message_to_browser(message) {
  550. socket_window.send(message);
  551. for (let i in allWindowSockets) {
  552. try {
  553. allWindowSockets[i].send(message);
  554. } catch {
  555. console.log("Cannot send to socket with id: ", allWindowScoketNames[i]);
  556. }
  557. }
  558. }
  559. const WebSocket = require('ws');
  560. const {all} = require("express/lib/application");
  561. let wss = new WebSocket.Server({port: websocket_port});
  562. wss.on('connection', function (ws) {
  563. ws.on('message', async function (message, isBinary) {
  564. let msg = JSON.parse(message.toString());
  565. // console.log("\n\nGET A MESSAGE: ", msg);
  566. // console.log(msg, msg.type, msg.message);
  567. if (msg.type == 0) {
  568. if (msg.message.id == 0) {
  569. socket_window = ws;
  570. // socket_window.on('close', function (event) {
  571. // socket_window = null;
  572. // console.log("socket_window closed");
  573. // });
  574. // console.log("set socket_window at time: ", new Date());
  575. } else if (msg.message.id == 1) {
  576. socket_start = ws;
  577. console.log("set socket_start at time: ", new Date());
  578. } else if (msg.message.id == 2) {
  579. socket_flowchart = ws;
  580. // socket_flowchart.on('close', function (event) {
  581. // socket_flowchart = null;
  582. // console.log("socket_flowchart closed");
  583. // });
  584. console.log("set socket_flowchart at time: ", new Date());
  585. } else { //其他的ID是用来标识不同的浏览器标签页的
  586. await new Promise(resolve => setTimeout(resolve, 2300));
  587. let handles = await driver.getAllWindowHandles();
  588. if (arrayDifference(handles, old_handles).length > 0) {
  589. old_handles = handles;
  590. current_handle = handles[handles.length - 1];
  591. console.log("New tab opened, change current_handle to: ", current_handle);
  592. }
  593. handle_pairs[msg.message.id] = current_handle;
  594. console.log("Set handle_pair for id: ", msg.message.id, " to ", current_handle, ", title is: ", msg.message.title);
  595. socket_flowchart.send(JSON.stringify({"type": "title", "data": {"title": msg.message.title}}));
  596. allWindowSockets.push(ws);
  597. allWindowScoketNames.push(msg.message.id);
  598. console.log("set socket for id: ", msg.message.id, " at time: ", new Date());
  599. ws.on('close', function (event) {
  600. let index = allWindowSockets.indexOf(ws);
  601. if (index > -1) {
  602. allWindowSockets.splice(index, 1);
  603. allWindowScoketNames.splice(index, 1);
  604. }
  605. console.log("socket for id: ", msg.message.id, " closed at time: ", new Date());
  606. });
  607. // console.log("handle_pairs: ", handle_pairs);
  608. }
  609. } else if (msg.type == 10) {
  610. let leave_handle = handle_pairs[msg.message.id];
  611. if (leave_handle != null && leave_handle != undefined && leave_handle != "") {
  612. await driver.switchTo().window(leave_handle);
  613. console.log("Switch to handle: ", leave_handle);
  614. current_handle = leave_handle;
  615. }
  616. } else {
  617. await beginInvoke(msg, ws);
  618. }
  619. });
  620. });
  621. console.log(process.platform);
  622. async function runBrowser(lang = "en", user_data_folder = '', mobile = false) {
  623. const serviceBuilder = new ServiceBuilder(driverPath);
  624. let options = new chrome.Options();
  625. options.addArguments('--disable-blink-features=AutomationControlled');
  626. language = lang;
  627. if (lang == "en") {
  628. options.addExtensions(path.join(__dirname, "EasySpider_en.crx"));
  629. } else if (lang == "zh") {
  630. options.addExtensions(path.join(__dirname, "EasySpider_zh.crx"));
  631. }
  632. options.addExtensions(path.join(__dirname, "XPathHelper.crx"));
  633. options.setChromeBinaryPath(chromeBinaryPath);
  634. if (user_data_folder != "") {
  635. let dir = path.join(task_server.getDir(), user_data_folder);
  636. console.log(dir);
  637. options.addArguments("--user-data-dir=" + dir);
  638. config.user_data_folder = user_data_folder;
  639. fs.writeFileSync(path.join(task_server.getDir(), "config.json"), JSON.stringify(config));
  640. }
  641. if (mobile) {
  642. const mobileEmulation = {
  643. deviceName: 'iPhone XR'
  644. };
  645. 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"`);
  646. options.setMobileEmulation(mobileEmulation);
  647. }
  648. driver = new Builder()
  649. .forBrowser('chrome')
  650. .setChromeOptions(options)
  651. .setChromeService(serviceBuilder)
  652. .build();
  653. await driver.manage().setTimeouts({implicit: 3, pageLoad: 10000, script: 10000});
  654. await driver.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
  655. // await driver.executeScript("localStorage.clear();"); //重置参数数量
  656. const cdpConnection = await driver.createCDPConnection("page");
  657. let stealth_path = path.join(__dirname, "stealth.min.js");
  658. let stealth = fs.readFileSync(stealth_path, 'utf8');
  659. await cdpConnection.execute('Page.addScriptToEvaluateOnNewDocument', {
  660. source: stealth,
  661. });
  662. try {
  663. if (mobile) {
  664. await driver.get(server_address + "/taskGrid/taskList.html?wsport=" + websocket_port + "&backEndAddressServiceWrapper=" + server_address + "&mobile=1&lang=" + lang);
  665. } else {
  666. await driver.get(server_address + "/taskGrid/taskList.html?wsport=" + websocket_port + "&backEndAddressServiceWrapper=" + server_address + "&lang=" + lang);
  667. }
  668. old_handles = await driver.getAllWindowHandles();
  669. current_handle = old_handles[old_handles.length - 1];
  670. } finally {
  671. // await driver.quit(); // 退出浏览器
  672. }
  673. }
  674. function handleOpenBrowser(event, lang = "en", user_data_folder = "", mobile = false) {
  675. const webContents = event.sender;
  676. const win = BrowserWindow.fromWebContents(webContents);
  677. runBrowser(lang, user_data_folder, mobile);
  678. let size = screen.getPrimaryDisplay().workAreaSize;
  679. let width = parseInt(size.width);
  680. let height = parseInt(size.height * 0.6);
  681. flowchart_window = new BrowserWindow({
  682. x: 0,
  683. y: 0,
  684. width: width,
  685. height: height,
  686. icon: iconPath,
  687. });
  688. let url = "";
  689. let id = -1;
  690. if (lang == "en") {
  691. url = server_address + `/taskGrid/FlowChart.html?id=${id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address + "&mobile=" + mobile.toString();
  692. } else if (lang == "zh") {
  693. url = server_address + `/taskGrid/FlowChart_CN.html?id=${id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address + "&mobile=" + mobile.toString();
  694. }
  695. // and load the index.html of the app.
  696. flowchart_window.loadURL(url, {extraHeaders: 'pragma: no-cache\n'});
  697. if (process.platform != "darwin") {
  698. flowchart_window.hide();
  699. }
  700. flowchart_window.on('close', function (event) {
  701. mainWindow.show();
  702. driver.quit();
  703. });
  704. }
  705. function handleOpenInvoke(event, lang = "en") {
  706. invoke_window = new BrowserWindow({icon: iconPath});
  707. let url = "";
  708. language = lang;
  709. if (lang == "en") {
  710. url = server_address + `/taskGrid/taskList.html?type=1&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address;
  711. } else if (lang == "zh") {
  712. url = server_address + `/taskGrid/taskList.html?type=1&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address + "&lang=zh";
  713. }
  714. // and load the index.html of the app.
  715. invoke_window.loadURL(url, {extraHeaders: 'pragma: no-cache\n'});
  716. invoke_window.maximize();
  717. mainWindow.hide();
  718. invoke_window.on('close', function (event) {
  719. mainWindow.show();
  720. });
  721. }
  722. // This method will be called when Electron has finished
  723. // initialization and is ready to create browser windows.
  724. // Some APIs can only be used after this event occurs.
  725. app.whenReady().then(() => {
  726. session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
  727. details.requestHeaders['Accept-Language'] = 'zh'
  728. callback({cancel: false, requestHeaders: details.requestHeaders})
  729. })
  730. ipcMain.on('start-design', handleOpenBrowser);
  731. ipcMain.on('start-invoke', handleOpenInvoke);
  732. ipcMain.on('accept-agreement', function (event, arg) {
  733. config.copyright = 1;
  734. fs.writeFileSync(path.join(task_server.getDir(), "config.json"), JSON.stringify(config));
  735. });
  736. createWindow();
  737. app.on('activate', function () {
  738. // On macOS it's common to re-create a window in the app when the
  739. // dock icon is clicked and there are no other windows open.
  740. if (BrowserWindow.getAllWindows().length === 0) {
  741. createWindow();
  742. }
  743. })
  744. })
  745. // Quit when all windows are closed, except on macOS. There, it's common
  746. // for applications and their menu bar to stay active until the user quits
  747. // explicitly with Cmd + Q.
  748. app.on('window-all-closed', function () {
  749. if (process.platform !== 'darwin') {
  750. app.quit();
  751. }
  752. })
  753. // In this file you can include the rest of your app's specific main process
  754. // code. You can also put them in separate files and require them here.
  755. function arrayDifference(arr1, arr2) {
  756. return arr1.filter(item => !arr2.includes(item));
  757. }