您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自動在Facebook上戳回朋友,帶有控制面板和統計功能
// ==UserScript== // @name Facebook 優化版自動戳回 // @namespace https://github.com/poterpan/tampermonkey-scripts/facebook-autopoke // @version 3.5.2 // @description 自動在Facebook上戳回朋友,帶有控制面板和統計功能 // @author PoterPan // @match https://www.facebook.com/pokes // @match https://www.facebook.com/pokes/* // @homepageURL https://github.com/poterpan/tampermonkey-scripts // @supportURL https://github.com/poterpan/tampermonkey-scripts/issues // ==/UserScript== (function() { 'use strict'; // 全局設置和計數變數 const settings = { enabled: true, // 是否啟用自動戳回 minDelay: 3, // 最小延遲(秒) maxDelay: 30, // 最大延遲(秒) idleMinDelay: 30, // 閒置狀態最小延遲(秒) idleMaxDelay: 90, // 閒置狀態最大延遲(秒) idleThreshold: 10 // 多少次無活動後進入閒置狀態 }; // 統計數據 const stats = { totalPokes: 0, // 總戳回次數 personalStats: {}, // 每個人的戳回次數 lastPokeTime: null, // 上次戳回時間 nextPokeTime: null, // 下次預計戳回時間 noActivityCount: 0, // 無活動計數 isIdle: false // 是否處於閒置狀態 }; // 追蹤已處理的按鈕 const processedButtons = new Set(); let controlPanel = null; // 控制面板引用 let timerInterval = null; // 倒計時計時器 // 儲存設定到 localStorage function saveSettings() { localStorage.setItem('fb_autopoke_settings', JSON.stringify(settings)); console.log('設定已儲存', settings); } // 儲存統計到 localStorage function saveStats() { localStorage.setItem('fb_autopoke_stats', JSON.stringify(stats)); } // 創建控制面板 function createControlPanel() { if (document.getElementById("fb_autopoke_panel")) return; // 主控制面板 const panel = document.createElement("div"); panel.id = "fb_autopoke_panel"; panel.style.position = "fixed"; panel.style.zIndex = "10000"; panel.style.right = "20px"; panel.style.top = "60px"; panel.style.width = "280px"; panel.style.backgroundColor = "#fff"; panel.style.border = "1px solid #dddfe2"; panel.style.borderRadius = "8px"; panel.style.boxShadow = "0 2px 4px rgba(0, 0, 0, 0.1)"; panel.style.fontFamily = "Arial, sans-serif"; panel.style.fontSize = "13px"; panel.style.color = "#1c1e21"; // 面板標題 const header = document.createElement("div"); header.style.padding = "10px"; header.style.borderBottom = "1px solid #dddfe2"; header.style.fontWeight = "bold"; header.style.backgroundColor = "#4267b2"; header.style.color = "#fff"; header.style.borderRadius = "8px 8px 0 0"; header.style.display = "flex"; header.style.justifyContent = "space-between"; header.style.alignItems = "center"; header.innerHTML = "Facebook 自動戳回控制面板"; // 最小化按鈕 const minButton = document.createElement("button"); minButton.textContent = "−"; minButton.style.background = "none"; minButton.style.border = "none"; minButton.style.color = "#fff"; minButton.style.fontWeight = "bold"; minButton.style.fontSize = "16px"; minButton.style.cursor = "pointer"; minButton.title = "最小化"; minButton.onclick = function() { const content = document.getElementById("fb_autopoke_content"); if (content.style.display === "none") { content.style.display = "block"; this.textContent = "−"; this.title = "最小化"; } else { content.style.display = "none"; this.textContent = "+"; this.title = "展開"; } }; header.appendChild(minButton); // 內容區域 const content = document.createElement("div"); content.id = "fb_autopoke_content"; content.style.padding = "10px"; // 操作區域 const controls = document.createElement("div"); controls.style.marginBottom = "10px"; // 開關按鈕 const toggleBtn = document.createElement("button"); toggleBtn.id = "fb_autopoke_toggle"; toggleBtn.textContent = settings.enabled ? "已啟用" : "已停用"; toggleBtn.style.padding = "5px 10px"; toggleBtn.style.marginRight = "10px"; toggleBtn.style.border = "none"; toggleBtn.style.borderRadius = "4px"; toggleBtn.style.backgroundColor = settings.enabled ? "#42b72a" : "#f5f6f7"; toggleBtn.style.color = settings.enabled ? "#fff" : "#4b4f56"; toggleBtn.style.cursor = "pointer"; toggleBtn.onclick = function() { settings.enabled = !settings.enabled; this.textContent = settings.enabled ? "已啟用" : "已停用"; this.style.backgroundColor = settings.enabled ? "#42b72a" : "#f5f6f7"; this.style.color = settings.enabled ? "#fff" : "#4b4f56"; if (settings.enabled) { scheduleNextPoke(3); // 啟用後3秒開始 } saveSettings(); }; controls.appendChild(toggleBtn); // 立即運行按鈕 const runNowBtn = document.createElement("button"); runNowBtn.textContent = "立即戳回"; runNowBtn.style.padding = "5px 10px"; runNowBtn.style.border = "none"; runNowBtn.style.borderRadius = "4px"; runNowBtn.style.backgroundColor = "#42b72a"; runNowBtn.style.color = "#fff"; runNowBtn.style.cursor = "pointer"; runNowBtn.onclick = function() { if (!settings.enabled) return; clearTimeout(window.pokeTimeout); runAutoPoke(); }; controls.appendChild(runNowBtn); // 狀態訊息 const statusDiv = document.createElement("div"); statusDiv.id = "fb_autopoke_status"; statusDiv.style.marginTop = "10px"; statusDiv.style.marginBottom = "10px"; statusDiv.style.padding = "8px"; statusDiv.style.backgroundColor = "#f5f6f7"; statusDiv.style.borderRadius = "4px"; statusDiv.style.fontSize = "12px"; // 設置區域 const settingsDiv = document.createElement("div"); settingsDiv.style.marginTop = "10px"; settingsDiv.style.marginBottom = "10px"; // 函數來創建設置項 function createSettingItem(label, id, value, min, max) { const item = document.createElement("div"); item.style.display = "flex"; item.style.justifyContent = "space-between"; item.style.alignItems = "center"; item.style.marginBottom = "5px"; const labelEl = document.createElement("label"); labelEl.htmlFor = id; labelEl.textContent = label; const input = document.createElement("input"); input.id = id; input.type = "number"; input.value = value; input.min = min; input.max = max; input.style.width = "60px"; input.style.padding = "3px"; input.onchange = function() { const val = parseInt(this.value); if (isNaN(val) || val < min) this.value = min; if (val > max) this.value = max; // 更新設置 const settingKey = id.replace('fb_autopoke_', ''); settings[settingKey] = parseInt(this.value); saveSettings(); // 記錄變更 addLog(`已設定 ${label} 為 ${this.value}`); }; item.appendChild(labelEl); item.appendChild(input); return item; } // 添加各種設置項 settingsDiv.appendChild(createSettingItem("正常最小延遲 (秒):", "fb_autopoke_minDelay", settings.minDelay, 1, 60)); settingsDiv.appendChild(createSettingItem("正常最大延遲 (秒):", "fb_autopoke_maxDelay", settings.maxDelay, 5, 300)); settingsDiv.appendChild(createSettingItem("閒置最小延遲 (秒):", "fb_autopoke_idleMinDelay", settings.idleMinDelay, 10, 300)); settingsDiv.appendChild(createSettingItem("閒置最大延遲 (秒):", "fb_autopoke_idleMaxDelay", settings.idleMaxDelay, 30, 600)); settingsDiv.appendChild(createSettingItem("閒置閾值 (無活動次數):", "fb_autopoke_idleThreshold", settings.idleThreshold, 3, 50)); // 日誌區域 const logDiv = document.createElement("div"); logDiv.id = "fb_autopoke_log"; logDiv.style.marginTop = "10px"; logDiv.style.padding = "8px"; logDiv.style.backgroundColor = "#f8f8f8"; logDiv.style.borderRadius = "4px"; logDiv.style.fontSize = "12px"; logDiv.style.maxHeight = "100px"; logDiv.style.overflowY = "auto"; logDiv.innerHTML = "<div>自動戳回日誌將顯示在這裡</div>"; // 統計區域 const statsDiv = document.createElement("div"); statsDiv.id = "fb_autopoke_stats"; statsDiv.style.marginTop = "10px"; statsDiv.style.fontSize = "12px"; statsDiv.innerHTML = "<div style='font-weight:bold;margin-bottom:5px;'>個人戳回統計:</div>"; // 個人統計列表 const statsList = document.createElement("div"); statsList.id = "fb_autopoke_stats_list"; statsList.style.maxHeight = "150px"; statsList.style.overflowY = "auto"; statsList.style.padding = "5px"; statsList.style.backgroundColor = "#f5f6f7"; statsList.style.borderRadius = "4px"; statsDiv.appendChild(statsList); // 清除統計按鈕 const clearStatsBtn = document.createElement("button"); clearStatsBtn.textContent = "清除統計"; clearStatsBtn.style.padding = "3px 8px"; clearStatsBtn.style.marginTop = "5px"; clearStatsBtn.style.border = "1px solid #dddfe2"; clearStatsBtn.style.borderRadius = "4px"; clearStatsBtn.style.backgroundColor = "#f5f6f7"; clearStatsBtn.style.cursor = "pointer"; clearStatsBtn.onclick = function() { stats.totalPokes = 0; stats.personalStats = {}; updateStatsList(); saveStats(); addLog("已清除所有統計資料"); }; statsDiv.appendChild(clearStatsBtn); // 組裝面板 content.appendChild(controls); content.appendChild(statusDiv); content.appendChild(settingsDiv); content.appendChild(logDiv); content.appendChild(statsDiv); panel.appendChild(header); panel.appendChild(content); document.body.appendChild(panel); // 保存面板引用 controlPanel = panel; // 更新面板上各項設定的值 updateSettingsDisplay(); // 更新顯示 updateStatus(); updateStatsList(); // 開始計時器 setInterval(updateStatus, 1000); return panel; } // 更新設定顯示 function updateSettingsDisplay() { document.getElementById("fb_autopoke_minDelay").value = settings.minDelay; document.getElementById("fb_autopoke_maxDelay").value = settings.maxDelay; document.getElementById("fb_autopoke_idleMinDelay").value = settings.idleMinDelay; document.getElementById("fb_autopoke_idleMaxDelay").value = settings.idleMaxDelay; document.getElementById("fb_autopoke_idleThreshold").value = settings.idleThreshold; // 更新啟用/停用按鈕狀態 const toggleBtn = document.getElementById("fb_autopoke_toggle"); if (toggleBtn) { toggleBtn.textContent = settings.enabled ? "已啟用" : "已停用"; toggleBtn.style.backgroundColor = settings.enabled ? "#42b72a" : "#f5f6f7"; toggleBtn.style.color = settings.enabled ? "#fff" : "#4b4f56"; } } // 添加日誌 function addLog(message) { const logDiv = document.getElementById("fb_autopoke_log"); if (!logDiv) return; const now = new Date(); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); const seconds = now.getSeconds().toString().padStart(2, '0'); const timestamp = `${hours}:${minutes}:${seconds}`; const logEntry = document.createElement("div"); logEntry.innerHTML = `<span style="color:#777;">[${timestamp}]</span> ${message}`; logDiv.appendChild(logEntry); // 滾動到底部 logDiv.scrollTop = logDiv.scrollHeight; // 限制日誌數量 while (logDiv.childNodes.length > 50) { logDiv.removeChild(logDiv.firstChild); } } // 更新狀態顯示 function updateStatus() { const statusDiv = document.getElementById("fb_autopoke_status"); if (!statusDiv) return; const now = new Date(); let status = ""; status += `<div>總計戳回: <b>${stats.totalPokes}</b> 次</div>`; if (stats.lastPokeTime) { const lastTime = new Date(stats.lastPokeTime); const timeDiff = Math.round((now - lastTime) / 1000); const lastTimeStr = formatTime(lastTime); status += `<div>上次戳回: <b>${lastTimeStr}</b> (${timeDiff}秒前)</div>`; } else { status += `<div>上次戳回: <b>無</b></div>`; } if (stats.nextPokeTime && settings.enabled) { const nextTime = new Date(stats.nextPokeTime); let timeLeft = Math.round((nextTime - now) / 1000); if (timeLeft < 0) timeLeft = 0; const nextTimeStr = formatTime(nextTime); status += `<div>下次檢查: <b>${nextTimeStr}</b> (<span id="fb_countdown">${timeLeft}</span>秒後)</div>`; status += `<div>狀態: <b>${stats.isIdle ? "閒置模式" : "活躍模式"}</b> (連續${stats.noActivityCount}次無活動)</div>`; } else { status += `<div>下次檢查: <b>已停用</b></div>`; } statusDiv.innerHTML = status; // 更新倒計時 const countdownEl = document.getElementById("fb_countdown"); if (countdownEl && stats.nextPokeTime) { let timeLeft = Math.round((new Date(stats.nextPokeTime) - now) / 1000); if (timeLeft < 0) timeLeft = 0; countdownEl.textContent = timeLeft; } } // 更新個人統計列表 function updateStatsList() { const statsList = document.getElementById("fb_autopoke_stats_list"); if (!statsList) return; // 將個人統計轉為陣列並按次數排序 const sortedStats = Object.entries(stats.personalStats) .sort((a, b) => b[1] - a[1]); if (sortedStats.length === 0) { statsList.innerHTML = "<div style='color:#777;font-style:italic;'>尚無數據</div>"; return; } let html = ""; sortedStats.forEach(([name, count]) => { html += `<div style='display:flex;justify-content:space-between;margin-bottom:3px;'> <span>${name}</span> <span><b>${count}</b> 次</span> </div>`; }); statsList.innerHTML = html; } // 格式化時間為 HH:MM:SS function formatTime(date) { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); const seconds = date.getSeconds().toString().padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; } // 嘗試從元素附近找到用戶名稱 function findUserName(element) { // 最基本的方法,查找附近的鏈接 let container = element; let maxDepth = 5; let depth = 0; // 向上查找容器 while (container && depth < maxDepth) { // 查找附近的用戶鏈接 const userLinks = container.querySelectorAll('a[href*="facebook.com/"]'); for (const link of userLinks) { const text = link.textContent.trim(); if (text && text !== "Facebook" && text !== "戳回去" && !text.includes("http")) { return text; } } // 從父元素的文本中尋找匹配「某人連續戳你 X 次」的模式 const contentText = container.textContent || ""; const pokeMatch = contentText.match(/([^\s,.!?]+)連續戳你\s*\d+\s*次/); if (pokeMatch && pokeMatch[1]) { return pokeMatch[1]; } // 從父元素的文本中尋找匹配「某人戳了你」的模式 const pokeMatch2 = contentText.match(/([^\s,.!?]+)戳了你/); if (pokeMatch2 && pokeMatch2[1]) { return pokeMatch2[1]; } container = container.parentElement; depth++; } return "未知用戶"; } // 安排下次戳回檢查 function scheduleNextPoke(seconds) { if (!settings.enabled) return; clearTimeout(window.pokeTimeout); if (!seconds) { // 根據當前狀態決定延遲時間 let minDelay, maxDelay; if (stats.isIdle) { minDelay = settings.idleMinDelay; maxDelay = settings.idleMaxDelay; } else { minDelay = settings.minDelay; maxDelay = settings.maxDelay; } seconds = minDelay + Math.round(Math.random() * (maxDelay - minDelay)); } addLog(`將在 ${seconds} 秒後進行下次戳回檢查`); // 更新下次戳回時間 stats.nextPokeTime = new Date(Date.now() + seconds * 1000).getTime(); saveStats(); // 更新顯示 updateStatus(); // 設置定時器 window.pokeTimeout = setTimeout(runAutoPoke, seconds * 1000); } // 主要戳回功能 function runAutoPoke() { if (!settings.enabled) return; addLog("執行自動戳回檢查..."); console.log("執行自動戳回檢查..."); // 確保控制面板存在 if (!controlPanel) { createControlPanel(); } // 找到所有具有戳回按鈕特徵的元素 let pokeButtons = []; // 方法1: 使用aria-label找戳回按鈕 const ariaButtons = document.querySelectorAll('[aria-label="戳回去"]'); pokeButtons = [...ariaButtons]; // 方法2: 查找包含 "戳回去" 文本的按鈕元素 if (pokeButtons.length === 0) { const allElements = document.querySelectorAll('div[role="button"]'); for (const el of allElements) { if (el.textContent && el.textContent.includes("戳回去")) { pokeButtons.push(el); } } } // 記錄找到的元素數量 addLog(`找到 ${pokeButtons.length} 個戳回按鈕`); // 已處理的用戶集,避免一次處理同一用戶多次 let processedUsers = new Set(); // 計數 let newPokes = 0; let pokedUsers = []; // 處理每個戳回按鈕 for (const button of pokeButtons) { // 嘗試找到用戶名稱 const userName = findUserName(button); // 如果這個用戶已經處理過,跳過 if (processedUsers.has(userName)) { continue; } try { // 標記為已處理 processedUsers.add(userName); // 更新統計 stats.totalPokes++; if (!stats.personalStats[userName]) { stats.personalStats[userName] = 0; } stats.personalStats[userName]++; // 點擊按鈕 button.click(); newPokes++; pokedUsers.push(userName); addLog(`已點擊「${userName}」的戳回按鈕`); console.log(`已點擊「${userName}」的戳回按鈕`); } catch (e) { addLog(`點擊出錯: ${e.message}`); console.error("點擊出錯:", e); } } // 更新統計信息 if (newPokes > 0) { stats.lastPokeTime = Date.now(); stats.noActivityCount = 0; stats.isIdle = false; addLog(`成功戳回 ${newPokes} 人: ${pokedUsers.join(', ')}`); } else { stats.noActivityCount++; if (stats.noActivityCount >= settings.idleThreshold) { stats.isIdle = true; } addLog(`沒有找到可戳回的用戶,無活動次數: ${stats.noActivityCount}`); } // 保存統計 saveStats(); // 更新顯示 updateStatus(); updateStatsList(); // 安排下次檢查 scheduleNextPoke(); } // 初始化 function initialize() { console.log("初始化 Facebook 自動戳回腳本..."); // 載入保存的設置 const savedSettings = localStorage.getItem('fb_autopoke_settings'); if (savedSettings) { try { const parsed = JSON.parse(savedSettings); Object.assign(settings, parsed); console.log("已載入儲存的設定:", settings); } catch (e) { console.error("載入設置失敗:", e); } } // 載入保存的統計 const savedStats = localStorage.getItem('fb_autopoke_stats'); if (savedStats) { try { const parsed = JSON.parse(savedStats); Object.assign(stats, parsed); console.log("已載入儲存的統計:", stats); } catch (e) { console.error("載入統計失敗:", e); } } // 創建控制面板 createControlPanel(); // 加入初次載入的日誌 addLog("腳本已初始化" + (settings.enabled ? ",自動戳回已啟用" : ",自動戳回目前停用")); // 如果設置為啟用,則開始自動戳回 if (settings.enabled) { // 初始延遲3秒,避免腳本重載時立即運行 scheduleNextPoke(3); } } // 在頁面載入完成後初始化 if (document.readyState === 'complete') { initialize(); } else { window.addEventListener('load', initialize); } })();