windows自动化新增基于C#的浏览器控制,和macos自动化下基于AppleScript的浏览器自动化一起作为备选方案

This commit is contained in:
fofolee 2025-01-19 13:43:42 +08:00
parent 1a1ec4b45f
commit 59b5c220ed
4 changed files with 346 additions and 4 deletions

View File

@ -11,6 +11,7 @@ const quickcomposer = {
windows: require("./quickcomposer/windows"),
macos: require("./quickcomposer/macos"),
status: require("./quickcomposer/status"),
browser: require("./quickcomposer/browser"),
};
module.exports = quickcomposer;

View File

@ -0,0 +1,296 @@
const { runCsharpFeature } = require("../../csharp");
const addressBarName = "地址和搜索栏";
const addressBarXpath =
"/Pane[3]/Pane/Pane[1]/Pane[2]/Pane[1]/ToolBar/Pane/Group/Edit";
const runBrowserAutomation = async (params) => {
const { value, browser = "msedge" } = params;
const baseArgs = ["-method", "process", "-window", browser];
if (value) {
baseArgs.push("-type", "setvalue", "-value", value, "-sendenter");
} else {
baseArgs.push("-type", "getvalue");
}
try {
return await runCsharpFeature("automation", [
...baseArgs,
"-name",
addressBarName,
]);
} catch (error) {
console.log(error);
try {
return await runCsharpFeature("automation", [
...baseArgs,
"-xpath",
addressBarXpath,
]);
} catch (error) {
console.log(error);
throw "获取浏览器地址栏失败";
}
}
};
const setUrl = async (browser = "msedge", url) => {
return await runBrowserAutomation({ value: url, browser });
};
const getUrl = async (browser = "msedge") => {
return await runBrowserAutomation({ browser });
};
const executeScript = async (browser = "msedge", script, args = {}) => {
// 构建参数列表
const argNames = Object.keys(args);
const argValues = Object.values(args).map((v) => JSON.stringify(v));
const wrapperScript = `
(function(${argNames.join(", ")}) {
${script}
})(${argValues.join(", ")})
`;
return await runBrowserAutomation({
value: `javascript:${encodeURIComponent(wrapperScript)}`,
browser,
});
};
// 点击元素
const clickElement = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
if (element) {
element.click();
return '##execute_script_success##';
} else {
return '##execute_script_failed##';
}
`;
return await executeScript(browser, script);
};
// 输入文本
const inputText = async (browser = "msedge", selector, text) => {
const script = `
const element = document.querySelector('${selector}');
if (element) {
element.value = '${text}';
element.dispatchEvent(new Event('input'));
element.dispatchEvent(new Event('change'));
return '##execute_script_success##';
} else {
return '##execute_script_failed##';
}
`;
return await executeScript(browser, script);
};
// 获取文本
const getText = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
return element ? element.textContent : '';
`;
return await executeScript(browser, script);
};
// 获取HTML
const getHtml = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
return element ? element.innerHTML : '';
`;
return await executeScript(browser, script);
};
// 隐藏元素
const hideElement = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
if (element) {
element.style.display = 'none';
return '##execute_script_success##';
} else {
return '##execute_script_failed##';
}
`;
return await executeScript(browser, script);
};
// 显示元素
const showElement = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
if (element) {
element.style.display = '';
return '##execute_script_success##';
} else {
return '##execute_script_failed##';
}
`;
return await executeScript(browser, script);
};
// 注入CSS
const injectCSS = async (browser = "msedge", css) => {
const script = `
const style = document.createElement('style');
style.textContent = \`${css}\`;
document.head.appendChild(style);
return '##execute_script_success##';
`;
return await executeScript(browser, script);
};
// 设置Cookie
const setCookie = async (browser = "msedge", cookies, options = {}) => {
const cookieStatements = cookies
.map((cookie) => {
let cookieString = `${cookie.name}=${cookie.value}`;
if (options.expires) {
const expiresDate = new Date(
Date.now() + options.expires * 60 * 60 * 1000
);
cookieString += `;expires=${expiresDate.toUTCString()}`;
}
if (options.path) cookieString += `;path=${options.path}`;
if (options.domain) cookieString += `;domain=${options.domain}`;
if (options.secure) cookieString += ";secure";
return `document.cookie = ${JSON.stringify(cookieString)};`;
})
.join("\n");
const script = `
${cookieStatements}
return '##execute_script_success##';
`;
return await this.executeScript(browser, script);
};
// 获取Cookie
const getCookie = async (browser = "msedge", name) => {
const script = `
const value = document.cookie
.split('; ')
.find(row => row.startsWith('${name}='))
?.split('=')[1];
return value || '';
`;
return await executeScript(browser, script);
};
// 删除Cookie
const deleteCookie = async (
browser = "msedge",
name,
path = "/",
domain = ""
) => {
const script = `
document.cookie = '${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT' +
(${domain ? `'; domain=${domain}'` : "''"}) +
(${path ? `'; path=${path}'` : "''"});
return '##execute_script_success##';
`;
return await executeScript(browser, script);
};
// 滚动到指定位置
const scrollTo = async (browser = "msedge", x, y) => {
const script = `
window.scrollTo(${x}, ${y});
return '##execute_script_success##';
`;
return await executeScript(browser, script);
};
// 滚动到元素位置
const scrollToElement = async (browser = "msedge", selector) => {
const script = `
const element = document.querySelector('${selector}');
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
return '##execute_script_success##';
} else {
return '##execute_script_failed##';
}
`;
return await executeScript(browser, script);
};
// 获取滚动位置
const getScrollPosition = async (browser = "msedge") => {
const script = `
return JSON.stringify({
x: window.pageXOffset || document.documentElement.scrollLeft,
y: window.pageYOffset || document.documentElement.scrollTop
});
`;
return await executeScript(browser, script);
};
// 获取页面尺寸
const getPageSize = async (browser = "msedge") => {
const script = `
return JSON.stringify({
width: Math.max(
document.documentElement.scrollWidth,
document.documentElement.clientWidth
),
height: Math.max(
document.documentElement.scrollHeight,
document.documentElement.clientHeight
)
});
`;
return await executeScript(browser, script);
};
// 等待元素出现
const waitForElement = async (browser = "msedge", selector, timeout = 5000) => {
const startTime = Date.now();
// 检查元素是否存在的函数
const checkScript = `
const element = document.querySelector('${selector}');
return element ? '##execute_script_success##' : '##execute_script_failed##';
`;
// 轮询检查元素
while (Date.now() - startTime < timeout) {
const result = await executeScript(browser, checkScript);
if (result === true) {
return true;
}
// 等待100ms再次检查
await new Promise((resolve) => setTimeout(resolve, 100));
}
return false;
};
module.exports = {
getUrl,
setUrl,
executeScript,
clickElement,
inputText,
getText,
getHtml,
hideElement,
showElement,
injectCSS,
setCookie,
getCookie,
deleteCookie,
scrollTo,
scrollToElement,
getScrollPosition,
getPageSize,
waitForElement,
};

View File

@ -7,6 +7,7 @@ const service = require("./service");
const software = require("./software");
const utils = require("./utils");
const automation = require("./automation");
const browser = require("./browser");
module.exports = {
window,
@ -18,4 +19,5 @@ module.exports = {
software,
utils,
automation,
browser,
};

View File

@ -1,5 +1,5 @@
import { newVarInputVal } from "js/composer/varInputValManager.js";
import { browserCommands } from "js/composer/commands/browserCommands.js";
const sendKeys = [
// 特殊按键
{ value: "{ENTER}", label: "回车键 (Enter)" },
@ -109,7 +109,7 @@ const registryPaths = [
const searchWindowConfig = [
{
label: "窗口查找方式",
component: "q-select",
component: "QSelect",
icon: "search",
width: 3,
options: [
@ -160,7 +160,7 @@ const searchElementConfig = [
...windowHandleConfig,
{
label: "元素查找方式",
component: "q-select",
component: "QSelect",
icon: "search",
width: 4,
options: [
@ -804,6 +804,49 @@ export const windowsCommands = {
},
],
},
// 浏览器
{
value: "quickcomposer.windows.browser.getUrl",
label: "浏览器控制",
icon: "language",
isAsync: true,
config: [
{
component: "ButtonGroup",
width: 12,
options: [
{ label: "Microsoft Edge", value: "msedge" },
{ label: "Google Chrome", value: "chrome" },
],
defaultValue: "msedge",
},
],
subCommands: browserCommands.commands
.find((command) => command.label === "浏览器操作")
.subCommands.map((command) => ({
...command,
value: command.value.replace(
"quickcomposer.browser.",
"quickcomposer.windows.browser."
),
}))
// 无法获得输出,过滤相关命令
.filter(
(command) =>
![
"getTabs",
"activateTab",
"getText",
"getHtml",
"getCookie",
"getScrollPosition",
"getPageSize",
"waitForElement",
]
.map((func) => `quickcomposer.windows.browser.${func}`)
.includes(command.value)
),
},
// 监控
{
value: "quickcomposer.windows.monitor.watchClipboard",
@ -1019,7 +1062,7 @@ export const windowsCommands = {
},
{
label: "值类型",
component: "q-select",
component: "QSelect",
icon: "category",
width: 4,
options: [