// ==UserScript== // @name 抖音直播间自动点赞 // @namespace https://scriptcat.org/ // @version 2.6.0 // @description 抖音直播间自动点赞小面板集成:选择点赞区/状态切换+间隔设置+次数设置+状态显示,实现真正动态随机间隔 // @author Xubai0224 // @match *://live.douyin.com/* // @icon https://www.douyin.com/favicon.ico // @icon64 https://www.douyin.com/favicon.ico // @grant GM_addStyle // @tag Douyin // @license MIT // @run-at document-idle // ==/UserScript== (function() { 'use strict'; /************************** * 1. 常量与全局状态管理 **************************/ // 全局配置默认值(常量) const DEFAULT_LIKE_CONFIG = { minInterval: 0.3, // 每组点赞最短间隔(秒) maxInterval: 1, // 每组点赞最长间隔(秒) maxCount: 1000, // 最大点赞组数(建议3000以内) isLiking: false // 是否正在点赞中 }; // 全局状态变量 const globalState = { clickPosition: { x: 0, y: 0 }, isSelectingArea: false, autoClickTimer: null, groupCount: 0, highlightMarker: null, countdownTimer: null, countdownNum: 3, isPreSelectCountdown: false, likeConfig: { ...DEFAULT_LIKE_CONFIG } // 深拷贝默认配置 }; /************************** * 2. 样式定义(模块化) **************************/ function injectStyles() { const styles = ` /* 核心小面板样式 */ #like-control-panel { position: fixed; top: 20px; right: 20px; width: 220px; background: rgba(255, 255, 255, 0.95); border: 1px solid #e5e7eb; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 114, 255, 0.3); z-index: 99999999; padding: 16px; font-family: system-ui, -apple-system, sans-serif; transition: all 0.3s ease; } /* 面板标题 */ .panel-title { font-size: 16px; font-weight: 600; color: #333; text-align: center; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #f3f4f6; } /* 功能按钮 */ #like-control-btn { width: 100%; padding: 8px 0; background: linear-gradient(135deg, #00c6ff, #0072ff); border: none; border-radius: 8px; color: white; font-size: 14px; cursor: pointer; transition: all 0.2s ease; margin-bottom: 12px; } #like-control-btn:hover { opacity: 0.9; transform: scale(1.02); } #like-control-btn:disabled { opacity: 0.7; cursor: not-allowed; transform: none; } /* 设置项容器 */ .setting-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; } /* 单个设置项 */ .setting-item { display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #666; } /* 输入框样式 */ .setting-input { width: 80px; padding: 4px 8px; border: 1px solid #e5e7eb; border-radius: 6px; font-size: 12px; text-align: center; outline: none; transition: border-color 0.2s ease; } .setting-input:focus { border-color: #0072ff; box-shadow: 0 0 0 2px rgba(0, 114, 255, 0.1); } /* 状态显示栏 */ #like-status-display { width: 100%; padding: 6px 0; background: #f9fafb; border-radius: 6px; text-align: center; font-size: 12px; color: #333; border: 1px solid #e5e7eb; transition: color 0.2s ease; } /* 坐标高亮标记样式 */ .coordinate-highlight { position: fixed; width: 30px; height: 30px; border: 2px solid #ff4d4f; border-radius: 50%; background: rgba(255, 77, 79, 0.2); z-index: 99999998; pointer-events: none; transform: translate(-50%, -50%); } /* 倒计时样式 */ .countdown-box { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 48px; font-weight: bold; color: #ff4d4f; text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); z-index: 99999999; pointer-events: none; } /* 坐标确认弹窗样式 */ .popup-box { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px 24px; background: rgba(255, 255, 255, 0.95); border: 1px solid #e5e7eb; border-radius: 12px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); z-index: 99999999; text-align: center; font-family: system-ui, -apple-system, sans-serif; } .popup-title { font-size: 16px; font-weight: 600; color: #333; margin-bottom: 16px; } .popup-btns { display: flex; justify-content: center; gap: 12px; } .popup-btn { padding: 6px 16px; border: none; border-radius: 8px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; } .popup-confirm-btn { background: #0072ff; color: white; } .popup-cancel-btn { background: #e5e7eb; color: #666; } .popup-btn:hover { opacity: 0.9; transform: scale(1.02); } /* 通用隐藏样式 */ .hidden { display: none !important; } /* Toast通知样式 */ .data-tool-notification { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 8px 16px; background: #4CAF50; color: white; border-radius: 4px; z-index: 99999; box-shadow: 0 2px 10px rgba(0,0,0,0.1); transition: opacity 0.3s; font-family: system-ui, -apple-system, sans-serif; font-size: 14px; } `; GM_addStyle(styles); } /************************** * 3. DOM元素创建(模块化) **************************/ function createDOMElements() { // 创建核心小面板 createControlPanel(); // 创建坐标确认弹窗 createConfirmPopup(); // 创建坐标高亮标记 createHighlightMarker(); // 创建倒计时元素 createCountdownBox(); // 绑定所有事件 bindAllEvents(); // 初始化状态显示 updateStatusDisplay(); } /** * 创建控制小面板 */ function createControlPanel() { const { likeConfig } = globalState; const panel = document.createElement('div'); panel.id = 'like-control-panel'; panel.innerHTML = `
抖音自动连点设置
最短间隔(秒):
最长间隔(秒):
最大点赞次数:
等待选择点赞区
`; document.body.appendChild(panel); } /** * 创建坐标确认弹窗 */ function createConfirmPopup() { const popup = document.createElement('div'); popup.id = 'coordinate-confirm-popup'; popup.className = 'popup-box hidden'; popup.innerHTML = ` `; document.body.appendChild(popup); } /** * 创建坐标高亮标记 */ function createHighlightMarker() { const marker = document.createElement('div'); marker.id = 'coordinate-highlight'; marker.className = 'coordinate-highlight hidden'; document.body.appendChild(marker); globalState.highlightMarker = marker; } /** * 创建倒计时元素 */ function createCountdownBox() { const countdownBox = document.createElement('div'); countdownBox.id = 'countdown-box'; countdownBox.className = 'countdown-box hidden'; document.body.appendChild(countdownBox); } /************************** * 4. 事件绑定(模块化) **************************/ function bindAllEvents() { // 获取DOM元素 const elements = { controlBtn: document.getElementById('like-control-btn'), confirmBtn: document.getElementById('confirm-btn'), cancelBtn: document.getElementById('cancel-btn'), minIntervalInput: document.getElementById('min-interval'), maxIntervalInput: document.getElementById('max-interval'), maxCountInput: document.getElementById('max-count'), confirmPopup: document.getElementById('coordinate-confirm-popup') }; // 功能按钮点击事件 elements.controlBtn.addEventListener('click', handleControlBtnClick); // 页面点击事件(获取点赞坐标) document.addEventListener('click', handlePageClick); // 确认弹窗-确认按钮事件 elements.confirmBtn.addEventListener('click', handleConfirmBtnClick); // 确认弹窗-取消按钮事件 elements.cancelBtn.addEventListener('click', handleCancelBtnClick); // 最短间隔输入框变更事件 elements.minIntervalInput.addEventListener('change', handleMinIntervalChange); // 最长间隔输入框变更事件 elements.maxIntervalInput.addEventListener('change', handleMaxIntervalChange); // 最大点赞次数输入框变更事件 elements.maxCountInput.addEventListener('change', handleMaxCountChange); } /** * 处理控制按钮点击事件 */ function handleControlBtnClick() { const { likeConfig } = globalState; const controlBtn = document.getElementById('like-control-btn'); // 若正在点赞,停止所有流程 if (likeConfig.isLiking) { stopAllProcess(); controlBtn.textContent = '选择点赞区域'; likeConfig.isLiking = false; updateStatusDisplay(); showToast('已手动停止点赞'); return; } // 停止现有流程 stopAllProcess(); // 进入选区前倒计时状态 globalState.isPreSelectCountdown = true; controlBtn.textContent = '准备选区中...'; controlBtn.disabled = true; showToast('即将进入选区模式,倒计时3秒...'); updateStatusDisplay(); // 启动选区前倒计时 startPreSelectCountdown(); } /** * 处理页面点击事件(获取点赞坐标) * @param {MouseEvent} e 鼠标事件对象 */ function handlePageClick(e) { if (!globalState.isSelectingArea) return; e.preventDefault(); // 保存点击坐标 globalState.clickPosition.x = e.clientX; globalState.clickPosition.y = e.clientY; // 退出选区状态 globalState.isSelectingArea = false; document.body.style.cursor = 'default'; // 高亮显示坐标 highlightCoordinate(globalState.clickPosition.x, globalState.clickPosition.y); // 显示确认弹窗 document.getElementById('coordinate-confirm-popup').classList.remove('hidden'); // 恢复按钮状态 const controlBtn = document.getElementById('like-control-btn'); controlBtn.textContent = '选择点赞区域'; controlBtn.disabled = false; } /** * 处理确认弹窗确认按钮点击事件 */ function handleConfirmBtnClick() { const { likeConfig } = globalState; const confirmPopup = document.getElementById('coordinate-confirm-popup'); const controlBtn = document.getElementById('like-control-btn'); // 隐藏弹窗和高亮标记 confirmPopup.classList.add('hidden'); globalState.highlightMarker.classList.add('hidden'); // 更新状态 likeConfig.isLiking = true; controlBtn.textContent = '停止点赞'; updateStatusDisplay(); showToast('3秒后将开始自动点赞,请稍候'); // 延迟显示倒计时 setTimeout(() => { startAutoLikeCountdown(); }, 2000); } /** * 处理确认弹窗取消按钮点击事件 */ function handleCancelBtnClick() { const confirmPopup = document.getElementById('coordinate-confirm-popup'); const controlBtn = document.getElementById('like-control-btn'); // 隐藏弹窗和高亮标记 confirmPopup.classList.add('hidden'); globalState.highlightMarker.classList.add('hidden'); // 恢复按钮状态 controlBtn.textContent = '选择点赞区域'; controlBtn.disabled = false; updateStatusDisplay(); showToast('已取消点赞区域选择'); } /** * 处理最短间隔输入框变更事件 * @param {Event} e 输入事件对象 */ function handleMinIntervalChange(e) { const { likeConfig } = globalState; const maxIntervalInput = document.getElementById('max-interval'); let value = parseFloat(e.target.value); // 数据校验与修正 if (isNaN(value)) value = DEFAULT_LIKE_CONFIG.minInterval; value = Math.max(0.1, Math.min(5, value)); // 更新配置 likeConfig.minInterval = value; e.target.value = value; // 同步修正最长间隔(确保最长≥最短) if (likeConfig.maxInterval < likeConfig.minInterval) { likeConfig.maxInterval = value; maxIntervalInput.value = value; } // 正在点赞时重启定时器 if (likeConfig.isLiking && globalState.autoClickTimer) { restartAutoLike(); } } /** * 处理最长间隔输入框变更事件 * @param {Event} e 输入事件对象 */ function handleMaxIntervalChange(e) { const { likeConfig } = globalState; let value = parseFloat(e.target.value); // 数据校验与修正 if (isNaN(value)) value = DEFAULT_LIKE_CONFIG.maxInterval; value = Math.max(likeConfig.minInterval, Math.min(5, value)); // 更新配置 likeConfig.maxInterval = value; e.target.value = value; // 正在点赞时重启定时器 if (likeConfig.isLiking && globalState.autoClickTimer) { restartAutoLike(); } } /** * 处理最大点赞次数输入框变更事件 * @param {Event} e 输入事件对象 */ function handleMaxCountChange(e) { const { likeConfig } = globalState; let value = parseInt(e.target.value); // 数据校验与修正 if (isNaN(value)) value = DEFAULT_LIKE_CONFIG.maxCount; value = Math.max(1, Math.min(999, value)); // 更新配置 likeConfig.maxCount = value; e.target.value = value; updateStatusDisplay(); } /************************** * 5. 通用工具函数 **************************/ /** * 显示Toast通知提示 * @param {string} message - 通知内容 */ function showToast(message) { // 移除现有通知 const existingToast = document.querySelector('.data-tool-notification'); if (existingToast) existingToast.remove(); // 创建新通知 const toast = document.createElement('div'); toast.className = 'data-tool-notification'; toast.textContent = message; document.body.appendChild(toast); // 自动消失逻辑 setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); } /** * 高亮显示指定坐标 * @param {number} x 横坐标 * @param {number} y 纵坐标 */ function highlightCoordinate(x, y) { const { highlightMarker } = globalState; highlightMarker.style.left = `${x}px`; highlightMarker.style.top = `${y}px`; highlightMarker.classList.remove('hidden'); } /** * 更新状态显示栏 */ function updateStatusDisplay() { const { likeConfig, groupCount } = globalState; const statusDisplay = document.getElementById('like-status-display'); if (likeConfig.isLiking) { statusDisplay.textContent = `点赞中 | 已完成${groupCount}/${likeConfig.maxCount}次`; statusDisplay.style.color = '#0072ff'; } else if (groupCount >= likeConfig.maxCount && groupCount > 0) { statusDisplay.textContent = `点赞结束 | 共完成${groupCount}/${likeConfig.maxCount}次`; statusDisplay.style.color = '#4CAF50'; } else { statusDisplay.textContent = '等待选择点赞区'; statusDisplay.style.color = '#333'; } } /************************** * 6. 流程控制函数 **************************/ /** * 停止所有正在执行的流程 */ function stopAllProcess() { const { countdownTimer, autoClickTimer, likeConfig } = globalState; const controlBtn = document.getElementById('like-control-btn'); // 停止倒计时 if (countdownTimer) { clearInterval(countdownTimer); globalState.countdownTimer = null; globalState.countdownNum = 3; document.getElementById('countdown-box').classList.add('hidden'); } // 停止自动连点 if (autoClickTimer) { clearTimeout(autoClickTimer); globalState.autoClickTimer = null; } // 重置状态变量 globalState.isPreSelectCountdown = false; globalState.isSelectingArea = false; likeConfig.isLiking = false; globalState.groupCount = 0; // 恢复页面样式和元素状态 document.body.style.cursor = 'default'; document.getElementById('coordinate-confirm-popup').classList.add('hidden'); globalState.highlightMarker.classList.add('hidden'); document.querySelector('.data-tool-notification')?.remove(); // 恢复按钮状态 if (controlBtn) { controlBtn.textContent = '选择点赞区域'; controlBtn.disabled = false; } // 更新状态显示 updateStatusDisplay(); } /** * 重启自动点赞流程(配置变更后生效) */ function restartAutoLike() { if (globalState.autoClickTimer) { clearTimeout(globalState.autoClickTimer); globalState.autoClickTimer = null; } startAutoDoubleClick(); } /** * 启动选区前3秒倒计时 */ function startPreSelectCountdown() { const countdownBox = document.getElementById('countdown-box'); globalState.countdownNum = 3; // 显示倒计时 countdownBox.textContent = globalState.countdownNum; countdownBox.classList.remove('hidden'); // 启动倒计时定时器 globalState.countdownTimer = setInterval(() => { globalState.countdownNum--; countdownBox.textContent = globalState.countdownNum; if (globalState.countdownNum <= 0) { clearInterval(globalState.countdownTimer); globalState.countdownTimer = null; countdownBox.classList.add('hidden'); globalState.isPreSelectCountdown = false; enterSelectAreaMode(); } }, 1000); } /** * 启动连点前3秒倒计时 */ function startAutoLikeCountdown() { const countdownBox = document.getElementById('countdown-box'); globalState.countdownNum = 3; // 显示倒计时 countdownBox.textContent = globalState.countdownNum; countdownBox.classList.remove('hidden'); // 启动倒计时定时器 globalState.countdownTimer = setInterval(() => { globalState.countdownNum--; if (globalState.countdownNum <= 0) { clearInterval(globalState.countdownTimer); globalState.countdownTimer = null; countdownBox.classList.add('hidden'); startAutoDoubleClick(); } else { countdownBox.textContent = globalState.countdownNum; } }, 1000); } /** * 进入选区模式 */ function enterSelectAreaMode() { globalState.isSelectingArea = true; showToast('请点击你要设置的点赞区域'); document.body.style.cursor = 'crosshair'; updateStatusDisplay(); } /************************** * 7. 核心业务函数 **************************/ /** * 模拟指定坐标的鼠标点击 * @param {number} x 横坐标 * @param {number} y 纵坐标 */ function simulateClick(x, y) { try { const targetElement = document.elementFromPoint(x, y); if (!targetElement) { console.warn('[自动连点] 未获取到目标元素,跳过本次点击'); return; } // 创建并触发原生鼠标点击事件 const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0 // 左键点击 }); targetElement.dispatchEvent(clickEvent); } catch (error) { console.error('[自动连点] 模拟点击失败:', error); } } /** * 启动自动双连点流程(递归实现动态随机间隔) */ function startAutoDoubleClick() { const { likeConfig } = globalState; // 重置当前组数 globalState.groupCount = 0; /** * 递归执行下一组连点 */ function executeNextLikeGroup() { const { clickPosition, groupCount } = globalState; // 执行当前组双连点(组内间隔10毫秒) simulateClick(clickPosition.x, clickPosition.y); setTimeout(() => { simulateClick(clickPosition.x, clickPosition.y); }, 10); // 更新组数和状态 globalState.groupCount++; updateStatusDisplay(); console.log(`[自动连点] 已完成第${globalState.groupCount}组双连点`); // 判断是否继续执行 if (globalState.groupCount < likeConfig.maxCount) { // 计算随机间隔(秒转毫秒) const nextRandomInterval = Math.random() * (likeConfig.maxInterval - likeConfig.minInterval) * 1000 + likeConfig.minInterval * 1000; // 递归设置下一组定时器 globalState.autoClickTimer = setTimeout(executeNextLikeGroup, nextRandomInterval); } else { // 完成所有点赞,恢复状态 globalState.autoClickTimer = null; likeConfig.isLiking = false; const controlBtn = document.getElementById('like-control-btn'); if (controlBtn) controlBtn.textContent = '选择点赞区域'; showToast(`已完成${likeConfig.maxCount}组双连点,点击结束!`); updateStatusDisplay(); } } // 启动第一组连点 executeNextLikeGroup(); } /************************** * 8. 初始化入口 **************************/ function initScript() { // 注入样式 injectStyles(); // 等待页面完全渲染后创建DOM setTimeout(() => { createDOMElements(); console.log('[指定区域自动双连点] 脚本初始化完成,小面板已就绪'); }, 1500); } // 启动脚本 initScript(); })();