新增通用的浏览器自动化扩展插件
This commit is contained in:
388
expand/general-auto/background.js
Normal file
388
expand/general-auto/background.js
Normal file
@@ -0,0 +1,388 @@
|
||||
// General Auto - Background Service Worker
|
||||
console.log('General Auto 后台服务已启动');
|
||||
|
||||
// 任务队列和状态管理
|
||||
const state = {
|
||||
taskQueue: [],
|
||||
isProcessing: false,
|
||||
requestLogs: [],
|
||||
maxLogs: 100
|
||||
};
|
||||
|
||||
// 监听来自 content script 的消息
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
console.log('收到消息:', request);
|
||||
|
||||
switch (request.action) {
|
||||
case 'executeTask':
|
||||
executeTask(request.task, sender.tab.id)
|
||||
.then(result => sendResponse({ success: true, data: result }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'executeScript':
|
||||
executeScript(request.code, sender.tab.id)
|
||||
.then(result => sendResponse({ success: true, data: result }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'getCookies':
|
||||
getCookies(request.url)
|
||||
.then(cookies => sendResponse({ success: true, cookies }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'setCookies':
|
||||
setCookies(request.cookies)
|
||||
.then(() => sendResponse({ success: true }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'clearCookies':
|
||||
clearCookies(request.url)
|
||||
.then(() => sendResponse({ success: true }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'navigateTo':
|
||||
navigateTo(request.url, sender.tab.id)
|
||||
.then(() => sendResponse({ success: true }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
|
||||
case 'getRequestLogs':
|
||||
sendResponse({ success: true, logs: state.requestLogs });
|
||||
break;
|
||||
|
||||
case 'clearRequestLogs':
|
||||
state.requestLogs = [];
|
||||
sendResponse({ success: true });
|
||||
break;
|
||||
|
||||
case 'getStatus':
|
||||
sendResponse({
|
||||
success: true,
|
||||
status: {
|
||||
taskCount: state.taskQueue.length,
|
||||
isProcessing: state.isProcessing,
|
||||
requestLogCount: state.requestLogs.length
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'logRequest':
|
||||
logRequest(request.data);
|
||||
sendResponse({ success: true });
|
||||
break;
|
||||
|
||||
default:
|
||||
sendResponse({ success: false, error: '未知操作' });
|
||||
}
|
||||
});
|
||||
|
||||
// 执行自动化任务
|
||||
async function executeTask(task, tabId) {
|
||||
try {
|
||||
const result = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabId },
|
||||
func: executeTaskInPage,
|
||||
args: [task]
|
||||
});
|
||||
return result[0].result;
|
||||
} catch (error) {
|
||||
console.error('执行任务失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 在页面中执行的任务函数
|
||||
function executeTaskInPage(task) {
|
||||
const { type, selector, action, value, timeout = 10000, waitVisible = true } = task;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
reject(new Error(`任务执行超时: ${timeout}ms`));
|
||||
}, timeout);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'click':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
el.click();
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '点击成功' });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'input':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
el.value = value;
|
||||
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '输入成功' });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'humanInput':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
humanTypeInput(el, value).then(() => {
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '人类输入成功' });
|
||||
});
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'getText':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, text: el.textContent.trim() });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'getAttribute':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
const attrValue = el.getAttribute(value);
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, value: attrValue });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'scroll':
|
||||
window.scrollBy({
|
||||
top: value.y || 0,
|
||||
left: value.x || 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '滚动成功' });
|
||||
break;
|
||||
|
||||
case 'scrollToElement':
|
||||
waitForElement(selector, timeout, false).then(el => {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '滚动到元素成功' });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'wait':
|
||||
setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: `等待 ${value}ms 完成` });
|
||||
}, value);
|
||||
break;
|
||||
|
||||
case 'waitForElement':
|
||||
waitForElement(selector, timeout, waitVisible).then(el => {
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, message: '元素已出现' });
|
||||
}).catch(err => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'evaluate':
|
||||
try {
|
||||
const result = eval(value);
|
||||
clearTimeout(timer);
|
||||
resolve({ success: true, result: result });
|
||||
} catch (err) {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
clearTimeout(timer);
|
||||
reject(new Error(`未知任务类型: ${type}`));
|
||||
}
|
||||
} catch (error) {
|
||||
clearTimeout(timer);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
// 等待元素出现
|
||||
function waitForElement(selector, timeout, checkVisible) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const check = () => {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
if (!checkVisible) {
|
||||
resolve(element);
|
||||
return true;
|
||||
}
|
||||
const rect = element.getBoundingClientRect();
|
||||
const isVisible = rect.width > 0 && rect.height > 0 &&
|
||||
window.getComputedStyle(element).visibility !== 'hidden' &&
|
||||
window.getComputedStyle(element).display !== 'none';
|
||||
if (isVisible) {
|
||||
resolve(element);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (check()) return;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
if (check()) {
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['style', 'class']
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
observer.disconnect();
|
||||
reject(new Error(`等待元素超时: ${selector}`));
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟人类输入
|
||||
function humanTypeInput(element, text, minDelay = 50, maxDelay = 150) {
|
||||
return new Promise((resolve) => {
|
||||
let index = 0;
|
||||
const type = () => {
|
||||
if (index < text.length) {
|
||||
element.value += text[index];
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
index++;
|
||||
const delay = minDelay + Math.random() * (maxDelay - minDelay);
|
||||
setTimeout(type, delay);
|
||||
} else {
|
||||
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
type();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 执行自定义脚本
|
||||
async function executeScript(code, tabId) {
|
||||
try {
|
||||
const result = await chrome.scripting.executeScript({
|
||||
target: { tabId: tabId },
|
||||
func: (code) => eval(code),
|
||||
args: [code]
|
||||
});
|
||||
return result[0].result;
|
||||
} catch (error) {
|
||||
console.error('执行脚本失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Cookie 管理
|
||||
async function getCookies(url) {
|
||||
if (url) {
|
||||
return await chrome.cookies.getAll({ url });
|
||||
} else {
|
||||
return await chrome.cookies.getAll({});
|
||||
}
|
||||
}
|
||||
|
||||
async function setCookies(cookies) {
|
||||
for (const cookie of cookies) {
|
||||
try {
|
||||
await chrome.cookies.set(cookie);
|
||||
} catch (error) {
|
||||
console.error('设置 Cookie 失败:', cookie, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function clearCookies(url) {
|
||||
let cookies;
|
||||
if (url) {
|
||||
cookies = await chrome.cookies.getAll({ url });
|
||||
} else {
|
||||
cookies = await chrome.cookies.getAll({});
|
||||
}
|
||||
|
||||
for (const cookie of cookies) {
|
||||
const protocol = cookie.secure ? 'https:' : 'http:';
|
||||
const cookieUrl = `${protocol}//${cookie.domain}${cookie.path}`;
|
||||
await chrome.cookies.remove({
|
||||
url: cookieUrl,
|
||||
name: cookie.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导航到指定 URL
|
||||
async function navigateTo(url, tabId) {
|
||||
await chrome.tabs.update(tabId, { url: url });
|
||||
}
|
||||
|
||||
// 记录请求
|
||||
function logRequest(data) {
|
||||
state.requestLogs.push({
|
||||
...data,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// 限制日志数量
|
||||
if (state.requestLogs.length > state.maxLogs) {
|
||||
state.requestLogs.shift();
|
||||
}
|
||||
}
|
||||
|
||||
// 监听标签页更新
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (changeInfo.status === 'complete') {
|
||||
console.log('页面加载完成:', tab.url);
|
||||
}
|
||||
});
|
||||
|
||||
// 监听网络请求
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
(details) => {
|
||||
// 可以在这里拦截和修改请求
|
||||
if (details.type === 'xmlhttprequest' || details.type === 'fetch') {
|
||||
console.log('API 请求:', details.url);
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls>"] },
|
||||
[]
|
||||
);
|
||||
|
||||
// 监听响应头
|
||||
chrome.webRequest.onCompleted.addListener(
|
||||
(details) => {
|
||||
if (details.type === 'xmlhttprequest' || details.type === 'fetch') {
|
||||
console.log('API 响应:', details.url, details.statusCode);
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls>"] },
|
||||
[]
|
||||
);
|
||||
Reference in New Issue
Block a user