423 lines
12 KiB
JavaScript
423 lines
12 KiB
JavaScript
// General Auto - Injected Script
|
|
(function() {
|
|
'use strict';
|
|
|
|
console.log('General Auto Injected script 已加载');
|
|
|
|
// 存储原始方法
|
|
const originalXHR = window.XMLHttpRequest;
|
|
const originalFetch = window.fetch;
|
|
const originalOpen = window.open;
|
|
|
|
// 请求拦截器配置
|
|
window.GeneralAutoConfig = {
|
|
interceptXHR: true,
|
|
interceptFetch: true,
|
|
logRequests: true,
|
|
requestCallbacks: [],
|
|
responseCallbacks: []
|
|
};
|
|
|
|
// 拦截 XMLHttpRequest
|
|
window.XMLHttpRequest = function() {
|
|
const xhr = new originalXHR();
|
|
const config = window.GeneralAutoConfig;
|
|
|
|
if (!config.interceptXHR) return xhr;
|
|
|
|
const originalOpen = xhr.open;
|
|
const originalSend = xhr.send;
|
|
const originalSetRequestHeader = xhr.setRequestHeader;
|
|
|
|
let requestData = {
|
|
method: '',
|
|
url: '',
|
|
headers: {},
|
|
body: null,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
xhr.open = function(method, url) {
|
|
requestData.method = method;
|
|
requestData.url = url;
|
|
return originalOpen.apply(this, arguments);
|
|
};
|
|
|
|
xhr.setRequestHeader = function(name, value) {
|
|
requestData.headers[name] = value;
|
|
return originalSetRequestHeader.apply(this, arguments);
|
|
};
|
|
|
|
xhr.send = function(body) {
|
|
requestData.body = body;
|
|
|
|
// 请求前回调
|
|
config.requestCallbacks.forEach(callback => {
|
|
try {
|
|
callback(requestData);
|
|
} catch (e) {
|
|
console.error('Request callback error:', e);
|
|
}
|
|
});
|
|
|
|
// 监听响应
|
|
this.addEventListener('load', function() {
|
|
const responseData = {
|
|
...requestData,
|
|
status: this.status,
|
|
statusText: this.statusText,
|
|
responseHeaders: this.getAllResponseHeaders(),
|
|
response: this.response,
|
|
responseText: this.responseText,
|
|
responseType: this.responseType,
|
|
responseURL: this.responseURL
|
|
};
|
|
|
|
// 响应后回调
|
|
config.responseCallbacks.forEach(callback => {
|
|
try {
|
|
callback(responseData);
|
|
} catch (e) {
|
|
console.error('Response callback error:', e);
|
|
}
|
|
});
|
|
|
|
// 发送到 background
|
|
if (config.logRequests) {
|
|
window.postMessage({
|
|
type: 'FROM_PAGE',
|
|
action: 'logRequest',
|
|
data: {
|
|
type: 'xhr',
|
|
method: responseData.method,
|
|
url: responseData.url,
|
|
status: responseData.status,
|
|
requestHeaders: responseData.headers,
|
|
requestBody: responseData.body,
|
|
responseHeaders: responseData.responseHeaders,
|
|
response: responseData.responseText
|
|
}
|
|
}, '*');
|
|
}
|
|
});
|
|
|
|
this.addEventListener('error', function() {
|
|
console.error('XHR Error:', requestData.url);
|
|
});
|
|
|
|
return originalSend.apply(this, arguments);
|
|
};
|
|
|
|
return xhr;
|
|
};
|
|
|
|
// 拦截 Fetch
|
|
window.fetch = function() {
|
|
const config = window.GeneralAutoConfig;
|
|
|
|
if (!config.interceptFetch) {
|
|
return originalFetch.apply(this, arguments);
|
|
}
|
|
|
|
const url = arguments[0];
|
|
const options = arguments[1] || {};
|
|
|
|
const requestData = {
|
|
method: options.method || 'GET',
|
|
url: typeof url === 'string' ? url : url.url,
|
|
headers: options.headers || {},
|
|
body: options.body,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
// 请求前回调
|
|
config.requestCallbacks.forEach(callback => {
|
|
try {
|
|
callback(requestData);
|
|
} catch (e) {
|
|
console.error('Request callback error:', e);
|
|
}
|
|
});
|
|
|
|
return originalFetch.apply(this, arguments).then(response => {
|
|
const clonedResponse = response.clone();
|
|
|
|
clonedResponse.text().then(body => {
|
|
const responseData = {
|
|
...requestData,
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
headers: Object.fromEntries(response.headers.entries()),
|
|
response: body,
|
|
responseURL: response.url
|
|
};
|
|
|
|
// 响应后回调
|
|
config.responseCallbacks.forEach(callback => {
|
|
try {
|
|
callback(responseData);
|
|
} catch (e) {
|
|
console.error('Response callback error:', e);
|
|
}
|
|
});
|
|
|
|
// 发送到 background
|
|
if (config.logRequests) {
|
|
window.postMessage({
|
|
type: 'FROM_PAGE',
|
|
action: 'logRequest',
|
|
data: {
|
|
type: 'fetch',
|
|
method: responseData.method,
|
|
url: responseData.url,
|
|
status: responseData.status,
|
|
requestHeaders: responseData.headers,
|
|
requestBody: responseData.body,
|
|
responseHeaders: responseData.headers,
|
|
response: body
|
|
}
|
|
}, '*');
|
|
}
|
|
});
|
|
|
|
return response;
|
|
});
|
|
};
|
|
|
|
// 提供全局自动化工具 API
|
|
window.AutomationUtils = {
|
|
// 模拟人类输入
|
|
humanInput: function(element, text, minDelay = 50, maxDelay = 150) {
|
|
return new Promise((resolve) => {
|
|
element.focus();
|
|
let index = 0;
|
|
const type = () => {
|
|
if (index < text.length) {
|
|
const char = text[index];
|
|
|
|
// 触发键盘事件
|
|
const keydownEvent = new KeyboardEvent('keydown', {
|
|
key: char,
|
|
code: `Key${char.toUpperCase()}`,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
const keypressEvent = new KeyboardEvent('keypress', {
|
|
key: char,
|
|
code: `Key${char.toUpperCase()}`,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
const keyupEvent = new KeyboardEvent('keyup', {
|
|
key: char,
|
|
code: `Key${char.toUpperCase()}`,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
|
|
element.dispatchEvent(keydownEvent);
|
|
element.dispatchEvent(keypressEvent);
|
|
|
|
element.value += char;
|
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
element.dispatchEvent(keyupEvent);
|
|
|
|
index++;
|
|
const delay = minDelay + Math.random() * (maxDelay - minDelay);
|
|
setTimeout(type, delay);
|
|
} else {
|
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
element.blur();
|
|
resolve();
|
|
}
|
|
};
|
|
setTimeout(type, 100);
|
|
});
|
|
},
|
|
|
|
// 模拟人类点击
|
|
humanClick: function(element, options = {}) {
|
|
return new Promise((resolve) => {
|
|
const rect = element.getBoundingClientRect();
|
|
const x = rect.left + (options.offsetX || rect.width * (0.3 + Math.random() * 0.4));
|
|
const y = rect.top + (options.offsetY || rect.height * (0.3 + Math.random() * 0.4));
|
|
|
|
// 滚动到元素
|
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
setTimeout(() => {
|
|
// 鼠标移动
|
|
const mousemoveEvent = new MouseEvent('mousemove', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
clientX: x,
|
|
clientY: y
|
|
});
|
|
element.dispatchEvent(mousemoveEvent);
|
|
|
|
setTimeout(() => {
|
|
// 鼠标按下
|
|
const mousedownEvent = new MouseEvent('mousedown', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
clientX: x,
|
|
clientY: y,
|
|
button: 0
|
|
});
|
|
element.dispatchEvent(mousedownEvent);
|
|
|
|
setTimeout(() => {
|
|
// 鼠标抬起
|
|
const mouseupEvent = new MouseEvent('mouseup', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
clientX: x,
|
|
clientY: y,
|
|
button: 0
|
|
});
|
|
element.dispatchEvent(mouseupEvent);
|
|
|
|
// 点击
|
|
const clickEvent = new MouseEvent('click', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true,
|
|
clientX: x,
|
|
clientY: y,
|
|
button: 0
|
|
});
|
|
element.dispatchEvent(clickEvent);
|
|
|
|
resolve();
|
|
}, 50 + Math.random() * 50);
|
|
}, 50 + Math.random() * 50);
|
|
}, 200 + Math.random() * 300);
|
|
});
|
|
},
|
|
|
|
// 等待元素出现
|
|
waitForElement: function(selector, timeout = 10000, checkVisible = true) {
|
|
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);
|
|
});
|
|
},
|
|
|
|
// 等待元素消失
|
|
waitForElementRemoved: function(selector, timeout = 10000) {
|
|
return new Promise((resolve, reject) => {
|
|
const check = () => {
|
|
const element = document.querySelector(selector);
|
|
if (!element) {
|
|
resolve();
|
|
return true;
|
|
}
|
|
const rect = element.getBoundingClientRect();
|
|
const isHidden = rect.width === 0 || rect.height === 0 ||
|
|
window.getComputedStyle(element).visibility === 'hidden' ||
|
|
window.getComputedStyle(element).display === 'none';
|
|
if (isHidden) {
|
|
resolve();
|
|
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);
|
|
});
|
|
},
|
|
|
|
// 随机延迟
|
|
randomDelay: function(min = 100, max = 500) {
|
|
return new Promise(resolve => {
|
|
setTimeout(resolve, min + Math.random() * (max - min));
|
|
});
|
|
},
|
|
|
|
// 获取所有元素
|
|
getElements: function(selector) {
|
|
return Array.from(document.querySelectorAll(selector));
|
|
},
|
|
|
|
// 监听请求
|
|
onRequest: function(callback) {
|
|
window.GeneralAutoConfig.requestCallbacks.push(callback);
|
|
},
|
|
|
|
// 监听响应
|
|
onResponse: function(callback) {
|
|
window.GeneralAutoConfig.responseCallbacks.push(callback);
|
|
},
|
|
|
|
// 清除所有回调
|
|
clearCallbacks: function() {
|
|
window.GeneralAutoConfig.requestCallbacks = [];
|
|
window.GeneralAutoConfig.responseCallbacks = [];
|
|
}
|
|
};
|
|
|
|
// 简写
|
|
window.AU = window.AutomationUtils;
|
|
|
|
console.log('AutomationUtils API 已就绪');
|
|
|
|
})();
|