您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
- // ==UserScript==
- // @name 网页基于xpath|文本的自动点击(优化版)
- // @namespace https://game.lpengine.cn/*
- // @version 1.0.2
- // @description 根据输入的XPath定位元素进行循环点击及按指定文本查找点击元素,优化了遍历框架查找元素、点击停止功能、执行顺序及延时机制,设置成分区形式,美化按钮样式,并添加每次点击时间显示功能,同时添加脚本保活机制。
- // @author toyourtomorrow
- // @match https://fz.lpengine.cn/*
- // @match https://game.lpengine.cn/*
- // @match *://*/*
- // @grant GM_setValue
- // @grant GM.getValue
- // @grant GM_addStyle
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- // 创建用于输入XPath、延时、指定文本以及显示状态的HTML元素,并设置样式使其在最上方显示,同时设置初始为可拖动状态,设置分区形式
- const inputDiv = document.createElement('div');
- inputDiv.style.position = 'fixed';
- inputDiv.style.top = '10px';
- inputDiv.style.left = '10px';
- inputDiv.style.zIndex = '9999';
- inputDiv.style.backgroundColor = '#f8f9fa';
- inputDiv.style.padding = '10px';
- inputDiv.style.borderRadius = '5px';
- inputDiv.style.boxShadow = '0 0 5px rgba(0, 0, 0, 0.2)';
- inputDiv.style.userSelect = 'none'; // 防止文本被误选
- inputDiv.draggable = true; // 设置可拖动
-
- inputDiv.innerHTML = `
- <div style="display: flex; flex-direction: column;">
- <!-- XPath 分区 -->
- <div style="background-color: #e9ecef; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
- <label for="xpathInput" style="margin-right: 10px;">XPath表达式:</label>
- <input type="text" id="xpathInput" style="padding: 5px; border: 1px solid #ced4da; border-radius: 3px; width: 300px;" />
- </div>
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
- <label for="xpathDelayInput" style="margin-right: 10px;">XPath延时(毫秒):</label>
- <input type="number" id="xpathDelayInput" style="padding: 5px; border: 1px solid #ced4da; border-radius: 3px; width: 100px;" value="300" />
- </div>
- <div style="display: flex; align-items: center;">
- <button id="startXPathButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #007bff; color: white; cursor: pointer;">开始点击(XPath)</button>
- <button id="stopXPathButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #dc3545; color: white; cursor: pointer;">停止点击(XPath)</button>
- </div>
- <span id="xpathStatus" style="margin-left: 10px; color: #6c757d; margin-top: 10px;">状态: 未开始</span>
- <span id="xpathLastClickTime" style="margin-left: 10px; color: #6c757d; margin-top: 5px;">上次点击时间(XPath): -</span>
- <span id="xpathNextClickTime" style="margin-left: 10px; color: 6c757d; margin-top: 5px;">下次点击时间(XPath): -</span>
- </div>
- <!-- 文本分区 -->
- <div style="background-color: #e9ecef; padding: 10px; border-radius: 5px;">
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
- <label for="textInput" style="margin-right: 10px;">指定文本:</label>
- <input type="text" id="textInput" style="padding: 5px; border: 1px solid #ced4da; border-radius: 3px; width: 200px;" />
- </div>
- <div style="display: flex; align-items: center; margin-bottom: 10px;">
- <label for="textDelayInput" style="margin-right: 10px;">文本延时(毫秒):</label>
- <input type="number" id="textDelayInput" style="padding: 5px; border: 1px solid #ced4da; border-radius: 3px; width: 100px;" value="300" />
- </div>
- <div style="display: flex; align-items: center;">
- <button id="startTextButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #28a745; color: white; cursor: pointer;">开始点击(文本)</button>
- <button id="stopTextButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #6c757d; color: white; cursor: pointer;">停止点击(文本)</button>
- </div>
- <span id="textStatus" style="margin-left: 10px; color: #6c757d; margin-top: 10px;">状态: 未开始</span>
- <span id="textLastClickTime" style="margin-left: 10px; color: #6c757d; margin-top: 5px;">上次点击时间(文本): -</span>
- <span id="textNextClickTime" style="margin-left: 10px; color: 6c757d; margin-top: 5px;">下次点击时间(文本): -</span>
- </div>
- <button id="closeButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #6c757d; color: white; cursor: pointer; margin-top: 15px;">关闭</button>
- <button id="minimizeButton" style="padding: 5px 10px; border: none; border-radius: 3px; background-color: #6c757d; color: white; cursor: pointer; margin-top: 5px;">缩小</button>
- </div>
- `;
- document.body.appendChild(inputDiv);
-
- // 用于记录鼠标按下时的坐标,实现拖动功能
- let startX, startY;
-
- // 为可拖动元素添加鼠标按下、移动和抬起事件监听器,正确实现拖动功能
- inputDiv.addEventListener('mousedown', function (e) {
- startX = e.clientX;
- startY = e.clientY;
- document.addEventListener('mousemove', drag);
- document.addEventListener('mouseup', stopDrag);
- });
-
- function drag(e) {
- const dx = e.clientX - startX;
- const dy = e.clientY - startY;
- inputDiv.style.left = (parseInt(inputDiv.style.left) || 0) + dx + 'px';
- inputDiv.style.top = (parseInt(inputDiv.style.top) || 0) + dy + 'px';
- startX = e.clientX;
- startY = e.clientY;
- }
-
- function stopDrag() {
- document.removeEventListener('mousemove', drag);
- document.removeEventListener('mouseup', stopDrag);
- }
-
- // 记录界面是否最小化
- let isMinimized = false;
-
- // 缩小按钮点击事件处理函数
- function minimize() {
- if (isMinimized) {
- // 如果已最小化,恢复原始大小,并重新显示文字相关元素
- inputDiv.style.width = 'auto';
- inputDiv.style.height = 'auto';
- inputDiv.querySelectorAll('span').forEach(span => span.style.display = '');
- isMinimized = false;
- } else {
- // 如果未最小化,缩小界面并隐藏文字相关元素
- inputDiv.style.width = '30px';
- inputDiv.style.height = '30px';
- inputDiv.querySelectorAll('span').forEach(span => span.style.display = 'none');
- isMinimized = true;
- }
- }
-
- // 获取相关DOM元素
- const xpathInput = document.getElementById('xpathInput');
- const xpathDelayInput = document.getElementById('xpathDelayInput');
- const startXPathButton = document.getElementById('startXPathButton');
- const stopXPathButton = document.getElementById('stopXPathButton');
- const textInput = document.getElementById('textInput');
- const textDelayInput = document.getElementById('textDelayInput');
- const startTextButton = document.getElementById('startTextButton');
- const stopTextButton = document.getElementById('stopTextButton');
- const closeButton = document.getElementById('closeButton');
- const minimizeButton = document.getElementById('minimizeButton');
- const xpathStatusSpan = document.getElementById('xpathStatus');
- const xpathLastClickTimeSpan = document.getElementById('xpathLastClickTime');
- const xpathNextClickTimeSpan = document.getElementById('xpathNextClickTime');
- const textStatusSpan = document.getElementById('textStatus');
- const textLastClickTimeSpan = document.getElementById('textLastClickTime');
- const textNextClickTimeSpan = document.getElementById('textNextClickTime');
-
- // 初始化保存的XPath值、延时值和指定文本值(如果存在)
- (async () => {
- const savedXPath = await GM.getValue('savedXPath', '');
- const savedXPathDelay = await GM.getValue('savedXPathDelay', 300);
- const savedText = await GM.getValue('savedText', '');
- const savedTextDelay = await GM.getValue('savedTextDelay', 300);
- xpathInput.value = savedXPath;
- xpathDelayInput.value = savedXPathDelay;
- textInput.value = savedText;
- textDelayInput.value = savedTextDelay;
- })();
-
- // 用于存储基于XPath点击的定时器标识和基于文本点击的定时器标识
- let xPathIntervalId = null;
- let textIntervalId = null;
-
- // 递归遍历所有节点(包括跨iframe、处理Shadow DOM等)查找元素的函数(XPath方式),优化错误处理
- function findElementsByXPathInAllContexts(xpath, context = document) {
- const results = [];
- const iterate = (node) => {
- try {
- const elements = document.evaluate(xpath, node, null, XPathResult.ANY_TYPE, null).iterateNext();
- while (elements) {
- results.push(elements);
- elements = document.evaluate(xpath, node, null, XPathResult.ANY_TYPE, null).iterateNext();
- }
- } catch (error) {
- console.error('XPath查找元素出现错误:', error);
- }
- const shadowRoots = node.querySelectorAll('*');
- shadowRoots.forEach((element) => {
- if (element.shadowRoot) {
- iterate(element.shadowRoot);
- }
- });
- const iframes = node.querySelectorAll('iframe');
- for (const iframe of iframes) {
- try {
- const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
- iterate(iframeDoc);
- } catch (error) {
- console.error('处理iframe时出现错误:', error);
- continue;
- }
- }
- };
- iterate(context);
- return results;
- }
-
- // 递归遍历所有节点(包括跨iframe、处理Shadow DOM等)查找包含指定文本元素的函数,优化错误处理
- function findElementsByTextInAllContexts(text, context = document) {
- const results = [];
- const iterate = (node) => {
- const elements = node.querySelectorAll('*');
- elements.forEach((element) => {
- try {
- if (element.textContent.includes(text)) {
- results.push(element);
- }
- } catch (error) {
- console.error('文本查找元素出现错误:', error);
- }
- });
- const shadowRoots = node.querySelectorAll('*');
- shadowRoots.forEach((element) => {
- if (element.shadowRoot) {
- iterate(element.shadowRoot);
- }
- });
- const iframes = node.querySelectorAll('iframe');
- for (const iframe of iframes) {
- try {
- const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
- iterate(iframeDoc);
- } catch (error) {
- console.error('处理iframe时出现错误:', error);
- continue;
- }
- }
- };
- iterate(context);
- return results;
- }
-
- // 等待页面加载完成(包括所有资源、iframe等加载完毕)后再执行查找等操作的函数
- function waitForPageLoad(callback) {
- if (document.readyState === 'complete') {
- callback();
- } else {
- window.addEventListener('load', callback);
- }
- }
-
- // 开始点击的函数(基于XPath),优化执行顺序和时间显示
- async function startClickingByXPath() {
- const xpath = xpathInput.value;
- const delay = parseInt(xpathDelayInput.value, 10);
- GM_setValue('savedXPath', xpath);
- GM_setValue('savedXPathDelay', delay);
-
- try {
- waitForPageLoad(() => {
- const startTime = Date.now();
- const doClick = async () => {
- const elements = findElementsByXPathInAllContexts(xpath);
- if (elements.length > 0) {
- for (const element of elements) {
- element.click();
- }
- const currentTime = Date.now();
- xpathLastClickTimeSpan.textContent = `上次点击时间(XPath): ${new Date(currentTime).toLocaleString()}`;
- xpathNextClickTimeSpan.textContent = `下次点击时间(XPath): ${new Date(currentTime + delay).toLocaleString()}`;
- } else {
- clearInterval(xPathIntervalId);
- alert('未找到匹配XPath的元素,已停止点击。');
- xpathStatusSpan.textContent = '状态: 已停止(未找到元素)';
- }
- };
- doClick();
- xPathIntervalId = setInterval(doClick, delay);
- xpathStatusSpan.textContent = '状态: 正在点击(XPath)';
- });
- } catch (error) {
- clearInterval(xPathIntervalId);
- alert('执行过程出现错误,请检查XPath表达式是否正确。');
- xpathStatusSpan.textContent = '状态: 已停止(出错)';
- }
- }
-
- // 开始点击的函数(基于指定文本),优化执行顺序和时间显示
- async function startClickingByText() {
- const text = textInput.value;
- const delay = parseInt(textDelayInput.value, 10);
- GM_setValue('savedText', text);
- GM_setValue('savedTextDelay', delay);
-
- try {
- waitForPageLoad(() => {
- const startTime = Date.now();
- const doClick = async () => {
- const elements = findElementsByTextInAllContexts(text);
- if (elements.length > 0) {
- for (const element of elements) {
- element.click();
- }
- const currentTime = Date.now();
- textLastClickTimeSpan.textContent = `上次点击时间(文本): ${new Date(currentTime).toLocaleString()}`;
- textNextClickTimeSpan.textContent = `下次点击时间(文本): ${new Date(currentTime + delay).toLocaleString()}`;
- } else {
- clearInterval(textIntervalId);
- alert('未找到包含指定文本的元素,已停止点击。');
- textStatusSpan.textContent = '状态: 已停止(未找到元素)';
- }
- };
- doClick();
- textIntervalId = setInterval(doClick, delay);
- textStatusSpan.textContent = '状态: 正在点击(文本)';
- });
- } catch (error) {
- clearInterval(textIntervalId);
- alert('执行过程出现错误,请检查输入的文本是否正确。');
- textStatusSpan.textContent = '状态: 已停止(出错)';
- }
- }
-
- // 为开始按钮(XPath方式)添加点击事件监听器
- startXPathButton.addEventListener('click', startClickingByXPath);
-
- // 为停止按钮(XPath方式)添加点击事件监听器
- stopXPathButton.addEventListener('click', () => {
- if (xPathIntervalId) {
- clearInterval(xPathIntervalId);
- xPathIntervalId = null;
- xpathStatusSpan.textContent = '状态: 已停止';
- }
- });
-
- // 为开始按钮(文本方式)添加点击事件监听器
- startTextButton.addEventListener('click', startClickingByText);
-
- // 为停止按钮(文本方式)添加点击事件监听器
- stopTextButton.addEventListener('click', () => {
- if (textIntervalId) {
- clearInterval(textIntervalId);
- textIntervalId = null;
- textStatusSpan.textContent = '状态: 已停止';
- }
- });
-
- // 为关闭按钮添加点击事件监听器,点击后移除整个输入框区域
- closeButton.addEventListener('click', () => {
- inputDiv.remove();
- });
-
- // 为缩小按钮添加点击事件监听器
- minimizeButton.addEventListener('click', minimize);
-
- // 脚本保活机制,定期执行一个简单的函数来保持脚本活跃,这里简单打印个信息示例,可按需调整具体逻辑
- setInterval(() => {
- console.log('脚本保持活跃');
- }, 60000); // 每60秒执行一次,可根据实际情况调整时间间隔
-
- })();