Files

389 lines
10 KiB
JavaScript

// 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>"] },
[]
);