// ==UserScript== // @name 抖音续火花自动发送助手-集成一言API和TXTAPI-支持多用户-增强版 // @namespace http://tampermonkey.net/ // @version 3.1.1 // @description 每天自动发送续火消息,支持自定义时间,集成一言API和TXTAPI,支持多目标用户,记录火花天数,专属一言,随机发送时间,用户列表解析,自动重试 // @author 飔梦 / 阚泥 / xiaohe123awa // @match https://creator.douyin.com/creator-micro/data/following/chat // @icon https://free.picui.cn/free/2025/11/23/69226264aca4e.png // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_listValues // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @connect hitokoto.cn // ==/UserScript== (function() { 'use strict'; // 默认配置 const DEFAULT_CONFIG = { baseMessage: "续火", sendTime: "00:01:00", sendTimeRandom: false, sendTimeRangeStart: "23:30:00", sendTimeRangeEnd: "00:30:00", checkInterval: 1000, maxWaitTime: 30000, maxRetryCount: 3, hitokotoTimeout: 60000, txtApiTimeout: 60000, useHitokoto: true, useTxtApi: true, useSpecialHitokoto: true, specialHitokotoMode: "random", specialHitokotoRandom: true, txtApiMode: "manual", txtApiManualRandom: true, customMessage: "—————每日续火—————\n\n[TXTAPI]\n\n—————每日一言—————\n\n[API]\n\n—————专属一言—————\n\n[专属一言]\n\n🔥 火花已续 [天数] 天", hitokotoFormat: "{hitokoto}\n—— {from}{from_who}", fromFormat: "{from}", fromWhoFormat: "「{from_who}」", txtApiUrl: "https://v1.hitokoto.cn/?encode=text", txtApiManualText: "文本1\n文本2\n文本3", enableTargetUser: false, targetUsernames: "", userSearchTimeout: 10000, maxHistoryLogs: 200, searchDebounceDelay: 500, searchThrottleDelay: 1000, clickMethod: "direct", pageLoadWaitTime: 5000, chatInputCheckInterval: 1000, multiUserMode: "sequential", multiUserRetrySame: false, fireDays: 1, lastFireDate: "", specialHitokotoMonday: "周一专属文案1\n周一专属文案2", specialHitokotoTuesday: "周二专属文案1\n周二专属文案2", specialHitokotoWednesday: "周三专属文案1\n周三专属文案2", specialHitokotoThursday: "周四专属文案1\n周四专属文案2", specialHitokotoFriday: "周五专属文案1\n周五专属文案2", specialHitokotoSaturday: "周六专属文案1\n周六专属文案2", specialHitokotoSunday: "周日专属文案1\n周日专属文案2", autoRetryEnabled: false, // 新增:自动重试开关 autoRetryInterval: 10, // 新增:自动重试间隔(分钟) retryAfterMaxReached: true, // 新增:达到最大重试后是否继续重试 retryResetInterval: 10 // 新增:清除重试计数并重试的间隔(分钟) }; // 状态变量 let isProcessing = false; let retryCount = 0; let countdownInterval = null; let isScriptCat = false; let userConfig = {}; let nextSendTime = null; let currentState = "idle"; let chatObserver = null; let searchTimeout = null; let lastSearchTime = 0; let searchDebounceTimer = null; let chatInputCheckTimer = null; // 多用户相关变量 let currentUserIndex = -1; let sentUsersToday = []; let allTargetUsers = []; let currentRetryUser = null; // 专属一言发送记录 let specialHitokotoSentIndexes = { monday: [], tuesday: [], wednesday: [], thursday: [], friday: [], saturday: [], sunday: [] }; // 拖动相关变量 let isDragging = false; let dragOffsetX = 0; let dragOffsetY = 0; let currentPanel = null; // 新增:自动重试相关变量 let autoRetryTimer = null; let lastRetryResetTime = 0; let isMaxRetryReached = false; // ==================== 核心功能函数 ==================== // 检测是否是ScriptCat function detectScriptCat() { return typeof ScriptCat !== 'undefined' || (typeof GM_info !== 'undefined' && GM_info.scriptHandler === 'ScriptCat'); } // 初始化配置 function initConfig() { const savedConfig = GM_getValue('userConfig'); userConfig = savedConfig ? { ...DEFAULT_CONFIG, ...savedConfig } : { ...DEFAULT_CONFIG }; for (const key in DEFAULT_CONFIG) { if (userConfig[key] === undefined) { userConfig[key] = DEFAULT_CONFIG[key]; } } if (!GM_getValue('txtApiManualSentIndexes')) { GM_setValue('txtApiManualSentIndexes', []); } if (!GM_getValue('historyLogs')) { GM_setValue('historyLogs', []); } // 初始化火花天数 if (!GM_getValue('fireDays')) { GM_setValue('fireDays', userConfig.fireDays); } else { userConfig.fireDays = GM_getValue('fireDays'); } // 初始化上次火花日期 if (!GM_getValue('lastFireDate')) { const today = new Date().toISOString().split('T')[0]; GM_setValue('lastFireDate', today); userConfig.lastFireDate = today; } else { userConfig.lastFireDate = GM_getValue('lastFireDate'); } // 初始化专属一言发送记录 if (!GM_getValue('specialHitokotoSentIndexes')) { GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); } else { specialHitokotoSentIndexes = GM_getValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); } // 初始化多用户数据 if (!GM_getValue('sentUsersToday')) { GM_setValue('sentUsersToday', []); } sentUsersToday = GM_getValue('sentUsersToday', []); if (!GM_getValue('currentUserIndex')) { GM_setValue('currentUserIndex', -1); } currentUserIndex = GM_getValue('currentUserIndex', -1); // 初始化重试计数 if (!GM_getValue('retryCount')) { GM_setValue('retryCount', 0); } retryCount = GM_getValue('retryCount', 0); // 初始化最大重试达到标志 if (!GM_getValue('isMaxRetryReached')) { GM_setValue('isMaxRetryReached', false); } isMaxRetryReached = GM_getValue('isMaxRetryReached', false); // 初始化上次重试重置时间 if (!GM_getValue('lastRetryResetTime')) { GM_setValue('lastRetryResetTime', 0); } lastRetryResetTime = GM_getValue('lastRetryResetTime', 0); // 解析目标用户列表 parseTargetUsers(); GM_setValue('userConfig', userConfig); return userConfig; } // 解析目标用户列表 function parseTargetUsers() { if (!userConfig.targetUsernames || !userConfig.targetUsernames.trim()) { allTargetUsers = []; userConfig.enableTargetUser = false; // 目标用户为空时自动关闭 return; } const rawText = userConfig.targetUsernames.trim(); allTargetUsers = rawText.split('\n') .map(user => user.trim()) .filter(user => user.length > 0); // 目标用户不为空时自动开启 if (allTargetUsers.length > 0) { userConfig.enableTargetUser = true; } addHistoryLog(`解析到 ${allTargetUsers.length} 个目标用户: ${allTargetUsers.join(', ')}`, 'info'); } // 获取下一个目标用户 function getNextTargetUser() { if (allTargetUsers.length === 0) { return null; } const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length === 0) { addHistoryLog('所有目标用户今日都已发送', 'info'); return null; } let nextUser; if (userConfig.multiUserMode === 'random') { const randomIndex = Math.floor(Math.random() * unsentUsers.length); nextUser = unsentUsers[randomIndex]; } else { if (currentUserIndex < 0 || currentUserIndex >= allTargetUsers.length) { currentUserIndex = 0; } let found = false; for (let i = 0; i < allTargetUsers.length; i++) { const index = (currentUserIndex + i) % allTargetUsers.length; const user = allTargetUsers[index]; if (!sentUsersToday.includes(user)) { nextUser = user; currentUserIndex = index; found = true; break; } } if (!found) { return null; } } return nextUser; } // 标记用户为已发送 function markUserAsSent(username) { if (!sentUsersToday.includes(username)) { sentUsersToday.push(username); GM_setValue('sentUsersToday', sentUsersToday); } const index = allTargetUsers.indexOf(username); if (index !== -1) { currentUserIndex = (index + 1) % allTargetUsers.length; GM_setValue('currentUserIndex', currentUserIndex); } addHistoryLog(`用户 ${username} 已标记为今日已发送`, 'success'); updateUserStatusDisplay(); } // 保存配置 function saveConfig() { GM_setValue('userConfig', userConfig); GM_setValue('fireDays', userConfig.fireDays); GM_setValue('lastFireDate', userConfig.lastFireDate); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', isMaxRetryReached); GM_setValue('lastRetryResetTime', lastRetryResetTime); } // 添加历史日志 function addHistoryLog(message, type = 'info') { const logs = GM_getValue('historyLogs', []); const logEntry = { timestamp: new Date().toISOString(), message: message, type: type }; logs.unshift(logEntry); if (logs.length > userConfig.maxHistoryLogs) { logs.splice(userConfig.maxHistoryLogs); } GM_setValue('historyLogs', logs); addLog(message, type); } // 获取历史日志 function getHistoryLogs() { return GM_getValue('historyLogs', []); } // 清空历史日志 function clearHistoryLogs() { GM_setValue('historyLogs', []); addHistoryLog('历史日志已清空', 'info'); } // 导出历史日志 function exportHistoryLogs() { const logs = getHistoryLogs(); const logText = logs.map(log => `${new Date(log.timestamp).toLocaleString()} [${log.type.toUpperCase()}] ${log.message}` ).join('\n'); const blob = new Blob([logText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `抖音续火助手日志_${new Date().toISOString().split('T')[0]}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); addHistoryLog('日志已导出', 'success'); } // 添加实时日志 function addLog(message, type = 'info') { const now = new Date(); const timeString = now.toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.style.color = type === 'success' ? '#00d8b8' : type === 'error' ? '#ff2c54' : '#ffc107'; logEntry.style.padding = '5px 0'; logEntry.style.borderBottom = '1px solid rgba(255,255,255,0.05)'; logEntry.textContent = `${timeString} - ${message}`; const logContainer = document.getElementById('dy-fire-log'); if (logContainer) { logContainer.prepend(logEntry); if (logContainer.children.length > 8) { logContainer.removeChild(logContainer.lastChild); } logContainer.scrollTop = 0; } } // 更新重试计数显示 function updateRetryCount() { const retryEl = document.getElementById('dy-fire-retry'); if (retryEl) { retryEl.textContent = `${retryCount}/${userConfig.maxRetryCount}`; } } // 更新一言状态显示 function updateHitokotoStatus(status, isSuccess = true) { const statusEl = document.getElementById('dy-fire-hitokoto'); if (statusEl) { statusEl.textContent = status; statusEl.style.color = isSuccess ? '#00d8b8' : '#ff2c54'; } } // 更新TXTAPI状态显示 function updateTxtApiStatus(status, isSuccess = true) { const statusEl = document.getElementById('dy-fire-txtapi'); if (statusEl) { statusEl.textContent = status; statusEl.style.color = isSuccess ? '#00d8b8' : '#ff2c54'; } } // 更新专属一言状态显示 function updateSpecialHitokotoStatus(status, isSuccess = true) { const statusEl = document.getElementById('dy-fire-special-hitokoto'); if (statusEl) { statusEl.textContent = status; statusEl.style.color = isSuccess ? '#00d8b8' : '#ff2c54'; } } // 更新火花天数显示 function updateFireDaysStatus() { const statusEl = document.getElementById('dy-fire-days'); if (statusEl) { statusEl.textContent = userConfig.fireDays; statusEl.style.color = '#00d8b8'; } } // 更新火花天数(每天第一次发送时调用) function updateFireDays() { const today = new Date().toISOString().split('T')[0]; const lastFireDate = userConfig.lastFireDate || ''; if (lastFireDate !== today) { // 新的一天,增加天数 userConfig.fireDays++; userConfig.lastFireDate = today; GM_setValue('fireDays', userConfig.fireDays); GM_setValue('lastFireDate', today); addHistoryLog(`新的一天开始,火花天数增加为: ${userConfig.fireDays}`, 'success'); updateFireDaysStatus(); } } // 初始化聊天列表观察器 function initChatObserver() { // 先停止旧的观察器,但不记录日志 if (chatObserver) { stopChatObserver('观察器初始化', true); // 传递skipLog为true } if (!userConfig.enableTargetUser || currentState !== 'searching') { addHistoryLog('观察器未启动: 目标用户功能未启用或当前状态非查找中', 'warn'); return; } // 获取当前目标用户信息 const currentTargetUser = currentRetryUser || getNextTargetUser(); const remainingUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); chatObserver = new MutationObserver(function(mutations) { clearTimeout(searchDebounceTimer); searchDebounceTimer = setTimeout(() => { const now = Date.now(); if (now - lastSearchTime < userConfig.searchThrottleDelay) { return; } lastSearchTime = now; // 添加查找统计 const searchStartTime = GM_getValue('searchStartTime', now); const searchDuration = now - searchStartTime; // 记录详细查找信息 if (searchDuration > 1000 && searchDuration % 5000 < 100) { // 每5秒记录一次 addHistoryLog(`正在查找用户: ${currentTargetUser || '未知'}, 已查找 ${Math.floor(searchDuration/1000)} 秒, DOM变化 ${mutations.length} 处`, 'info'); } findAndClickTargetUser(); }, userConfig.searchDebounceDelay); }); const chatContainer = findChatContainer(); if (chatContainer) { try { chatObserver.observe(chatContainer, { childList: true, subtree: true, attributes: false, characterData: false }); // 记录更详细的启动信息 const targetCount = allTargetUsers.length; const sentCount = sentUsersToday.length; const remaining = targetCount - sentCount; GM_setValue('searchStartTime', Date.now()); addHistoryLog(`聊天列表观察器已启动 (目标: ${currentTargetUser || '获取中...'}, 待发送用户: ${remaining}/${targetCount}, 容器: ${chatContainer.tagName}.${chatContainer.className || 'no-class'})`, 'info'); } catch (error) { addHistoryLog(`观察器启动失败: ${error.message}`, 'error'); chatObserver = null; } } else { addHistoryLog('未找到聊天列表容器,将使用备用查找策略', 'warn'); try { chatObserver.observe(document.body, { childList: true, subtree: false, attributes: false, characterData: false }); addHistoryLog('聊天列表观察器已启动(备用策略,监控body变化)', 'info'); } catch (error) { addHistoryLog(`备用观察器启动失败: ${error.message}`, 'error'); chatObserver = null; } } } // 查找聊天容器 function findChatContainer() { const possibleSelectors = [ '.chat-list-container', '.semi-list', '[role="list"]', '.conversation-list', '.message-list' ]; for (const selector of possibleSelectors) { const container = document.querySelector(selector); if (container) { return container; } } const sampleUser = document.querySelector('.item-header-name-vL_79m'); if (sampleUser) { let parent = sampleUser; for (let i = 0; i < 10; i++) { parent = parent.parentElement; if (parent && parent.children.length > 5) { return parent; } if (!parent) break; } } return null; } // 停止聊天观察器 function stopChatObserver(reason = '未知原因', skipLog = false) { if (chatObserver) { try { chatObserver.disconnect(); // 记录观察器详细信息 if (!skipLog) { const targetCount = allTargetUsers.length; const sentCount = sentUsersToday.length; const currentTarget = currentRetryUser || getNextTargetUser(); addHistoryLog(`聊天列表观察器已停止 (原因: ${reason}, 目标用户: ${targetCount}个, 已发送: ${sentCount}/${targetCount}, 当前目标: ${currentTarget || '无'})`, 'info'); } } catch (error) { if (!skipLog) { addHistoryLog(`停止观察器时出错: ${error.message} (原因: ${reason})`, 'error'); } } chatObserver = null; } else { if (!skipLog) { addHistoryLog(`尝试停止聊天观察器但观察器不存在 (原因: ${reason})`, 'warn'); } } if (searchDebounceTimer) { clearTimeout(searchDebounceTimer); searchDebounceTimer = null; } if (searchTimeout) { clearTimeout(searchTimeout); searchTimeout = null; } if (!skipLog && reason !== '观察器初始化') { // 添加状态信息 const stateInfo = { 'idle': '空闲', 'searching': '查找中', 'found': '已找到用户', 'sending': '发送中', 'processing': '处理中' }; addHistoryLog(`当前状态: ${stateInfo[currentState] || currentState}, 重试次数: ${retryCount}/${userConfig.maxRetryCount}`, 'info'); } } // 安全地创建鼠标事件 function createSafeMouseEvent(type, options = {}) { try { const safeOptions = { bubbles: true, cancelable: true, view: window, ...options }; return new MouseEvent(type, safeOptions); } catch (error) { try { const safeOptions = { bubbles: true, cancelable: true, ...options }; delete safeOptions.view; return new MouseEvent(type, safeOptions); } catch (error2) { addHistoryLog(`创建鼠标事件失败: ${error2.message}`, 'error'); return null; } } } // 添加查找开始时间记录 function startUserSearch() { GM_setValue('searchStartTime', Date.now()); GM_setValue('searchMutationCount', 0); GM_setValue('searchAttemptCount', 0); } // 查找并点击目标用户 function findAndClickTargetUser() { if (!userConfig.enableTargetUser || allTargetUsers.length === 0) { updateUserStatus('配置错误', false); return false; } if (currentState !== 'searching') { return false; } // 增加查找尝试计数 let searchAttemptCount = GM_getValue('searchAttemptCount', 0) + 1; GM_setValue('searchAttemptCount', searchAttemptCount); if (searchAttemptCount > 50) { addHistoryLog(`查找尝试次数过多(${searchAttemptCount}),可能DOM结构已变化`, 'error'); stopChatObserver('查找尝试次数过多'); return false; } let currentTargetUser; if (userConfig.multiUserRetrySame && retryCount > 1 && currentRetryUser) { // 重试时使用同一用户 currentTargetUser = currentRetryUser; addHistoryLog(`重试使用同一用户: ${currentTargetUser}`, 'info'); } else { currentTargetUser = getNextTargetUser(); currentRetryUser = currentTargetUser; } if (!currentTargetUser) { addHistoryLog('没有可发送的目标用户', 'info'); updateUserStatus('无目标用户', false); stopChatObserver(); isProcessing = false; currentRetryUser = null; return false; } GM_setValue('lastTargetUser', currentTargetUser); addHistoryLog(`查找目标用户: ${currentTargetUser}`, 'info'); updateUserStatus(`寻找: ${currentTargetUser}`, null); const userElements = document.querySelectorAll('.item-header-name-vL_79m'); let targetElement = null; for (let element of userElements) { if (element.textContent.trim() === currentTargetUser) { targetElement = element; break; } } if (targetElement) { addHistoryLog(`找到目标用户: ${currentTargetUser}`, 'success'); updateUserStatus(`已找到: ${currentTargetUser}`, true); stopChatObserver(); let clickSuccess = false; if (userConfig.clickMethod === 'direct') { try { targetElement.click(); addHistoryLog('使用直接点击方法成功', 'success'); clickSuccess = true; } catch (error) { addHistoryLog(`直接点击失败: ${error.message}`, 'error'); } } else { try { const clickEvent = createSafeMouseEvent('click'); if (clickEvent) { targetElement.dispatchEvent(clickEvent); addHistoryLog('使用事件触发方法成功', 'success'); clickSuccess = true; } else { targetElement.click(); addHistoryLog('事件创建失败,使用直接点击成功', 'success'); clickSuccess = true; } } catch (error) { addHistoryLog(`事件触发失败: ${error.message}`, 'error'); } } if (clickSuccess) { currentState = 'found'; stopChatObserver('成功找到目标用户'); waitForPageLoad().then(() => { addHistoryLog('页面加载完成,开始查找聊天输入框', 'info'); tryFindChatInput(); }).catch(error => { addHistoryLog(`等待页面加载超时: ${error.message}`, 'error'); tryFindChatInput(); }); return true; } else { try { let clickableParent = targetElement; for (let i = 0; i < 5; i++) { clickableParent = clickableParent.parentElement; if (!clickableParent) break; const style = window.getComputedStyle(clickableParent); if (style.cursor === 'pointer' || clickableParent.onclick) { clickableParent.click(); addHistoryLog('通过父元素点击成功', 'success'); currentState = 'found'; waitForPageLoad().then(() => { addHistoryLog('页面加载完成,开始查找聊天输入框', 'info'); tryFindChatInput(); }).catch(error => { addHistoryLog(`等待页面加载超时: ${error.message}`, 'error'); tryFindChatInput(); }); return true; } } } catch (error) { addHistoryLog(`父元素点击也失败: ${error.message}`, 'error'); } updateUserStatus('点击失败', false); return false; } } else { addHistoryLog(`未找到目标用户: ${currentTargetUser}`, 'warn'); updateUserStatus(`寻找: ${currentTargetUser}`, null); return false; } } // 等待页面加载完成 function waitForPageLoad() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error(`页面加载等待超时 (${userConfig.pageLoadWaitTime}ms)`)); }, userConfig.pageLoadWaitTime); if (document.readyState === 'complete') { clearTimeout(timeout); resolve(); return; } window.addEventListener('load', function onLoad() { clearTimeout(timeout); window.removeEventListener('load', onLoad); resolve(); }); let checkCount = 0; const maxChecks = userConfig.pageLoadWaitTime / 100; const checkInterval = setInterval(() => { checkCount++; const chatInput = document.querySelector('.chat-input-dccKiL'); if (chatInput) { clearTimeout(timeout); clearInterval(checkInterval); resolve(); return; } if (checkCount >= maxChecks) { clearTimeout(timeout); clearInterval(checkInterval); reject(new Error('页面DOM变化检查超时')); } }, 100); }); } // 发送消息函数 async function sendMessage() { if (isProcessing) { addHistoryLog('已有任务正在进行中', 'error'); return; } // 重置最大重试达到标志 isMaxRetryReached = false; GM_setValue('isMaxRetryReached', false); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length === 0) { addHistoryLog('所有目标用户今日都已发送', 'info'); return; } } else { const lastSentDate = GM_getValue('lastSentDate', ''); const today = new Date().toDateString(); if (lastSentDate === today) { addHistoryLog('今天已经发送过消息', 'info'); return; } } isProcessing = true; retryCount = 0; currentState = 'idle'; updateRetryCount(); addHistoryLog('开始发送流程...', 'info'); executeSendProcess(); } // 执行发送流程 async function executeSendProcess() { // 检查是否已达到最大重试次数 if (isMaxRetryReached && userConfig.retryAfterMaxReached) { // 检查是否到了自动重试时间 const now = Date.now(); const intervalMs = userConfig.autoRetryInterval * 60 * 1000; if (now - lastRetryResetTime < intervalMs) { const remainingMinutes = Math.ceil((intervalMs - (now - lastRetryResetTime)) / 60000); addHistoryLog(`已达到最大重试次数,${remainingMinutes}分钟后将自动重试`, 'info'); isProcessing = false; return; } else { // 重置重试计数 retryCount = 0; isMaxRetryReached = false; GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', false); addHistoryLog('自动重试时间已到,重置重试计数', 'info'); } } retryCount++; GM_setValue('retryCount', retryCount); updateRetryCount(); if (retryCount > userConfig.maxRetryCount) { if (userConfig.retryAfterMaxReached) { isMaxRetryReached = true; lastRetryResetTime = Date.now(); GM_setValue('isMaxRetryReached', true); GM_setValue('lastRetryResetTime', lastRetryResetTime); addHistoryLog(`已达到最大重试次数 (${userConfig.maxRetryCount}),${userConfig.autoRetryInterval}分钟后将自动重试`, 'error'); // 启动自动重试计时器 startAutoRetryTimer(); } else { addHistoryLog(`已达到最大重试次数 (${userConfig.maxRetryCount}),停止重试`, 'error'); } isProcessing = false; currentState = 'idle'; stopChatObserver('达到最大重试次数'); currentRetryUser = null; return; } addHistoryLog(`尝试发送 (${retryCount}/${userConfig.maxRetryCount})`, 'info'); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { currentState = 'searching'; const searchTimeoutId = setTimeout(() => { if (currentState === 'searching') { addHistoryLog('用户查找超时', 'error'); updateUserStatus('查找超时', false); stopChatObserver('用户查找超时'); // 这里记录日志 setTimeout(executeSendProcess, 2000); } }, userConfig.userSearchTimeout); initChatObserver(); const found = findAndClickTargetUser(); if (!found) { // 用户查找失败,观察器会继续工作 } } else { setTimeout(tryFindChatInput, 1000); } } // 启动自动重试计时器 function startAutoRetryTimer() { if (autoRetryTimer) { clearTimeout(autoRetryTimer); } if (!userConfig.retryAfterMaxReached) { return; } const intervalMs = userConfig.autoRetryInterval * 60 * 1000; autoRetryTimer = setTimeout(() => { if (isMaxRetryReached && !isProcessing) { addHistoryLog('自动重试计时器触发,重置重试计数并重新发送', 'info'); // 重置重试计数 retryCount = 0; isMaxRetryReached = false; GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', false); updateRetryCount(); // 重新发送 sendMessage(); } }, intervalMs); } // 重置重试计数并发送(用于定时任务) function resetRetryAndSend() { if (isProcessing) { addHistoryLog('已有任务正在进行中,跳过重置重试', 'info'); return; } // 重置重试计数 retryCount = 0; isMaxRetryReached = false; lastRetryResetTime = Date.now(); GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', false); GM_setValue('lastRetryResetTime', lastRetryResetTime); updateRetryCount(); addHistoryLog(`已重置重试计数,当前重试次数: ${retryCount}`, 'success'); // 检查是否需要发送 autoSendIfNeeded(); } // 尝试查找聊天输入框并发送消息 let chatInputRetryCount = 0; async function tryFindChatInput() { if (chatInputCheckTimer) { clearTimeout(chatInputCheckTimer); } const input = document.querySelector('.chat-input-dccKiL'); if (input) { chatInputRetryCount = 0; addHistoryLog('找到聊天输入框', 'info'); let messageToSend; try { messageToSend = await getMessageContent(); addHistoryLog('消息内容准备完成', 'success'); } catch (error) { addHistoryLog(`消息获取失败: ${error.message}`, 'error'); messageToSend = `${userConfig.baseMessage} | 消息获取失败~`; } currentState = 'sending'; input.textContent = ''; input.focus(); const lines = messageToSend.split('\n'); for (let i = 0; i < lines.length; i++) { document.execCommand('insertText', false, lines[i]); if (i < lines.length - 1) { document.execCommand('insertLineBreak'); } } input.dispatchEvent(new Event('input', { bubbles: true })); setTimeout(() => { const sendBtn = document.querySelector('.chat-btn'); if (sendBtn && !sendBtn.disabled) { addHistoryLog('正在发送消息...', 'info'); sendBtn.click(); setTimeout(() => { addHistoryLog('消息发送成功!', 'success'); // 更新火花天数(如果是今天第一次发送) updateFireDays(); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { const currentTargetUser = GM_getValue('lastTargetUser', ''); if (currentTargetUser) { markUserAsSent(currentTargetUser); } } else { const today = new Date().toDateString(); GM_setValue('lastSentDate', today); updateUserStatusDisplay(); } updateStatus(true); isProcessing = false; currentState = 'idle'; if (chatObserver) { stopChatObserver('消息发送成功'); } else { addHistoryLog('消息发送完成,观察器已不存在无需停止', 'info'); } currentRetryUser = null; // 重置重试计数 retryCount = 0; isMaxRetryReached = false; GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', false); updateRetryCount(); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length > 0) { addHistoryLog(`还有 ${unsentUsers.length} 个用户待发送,继续下一个用户`, 'info'); setTimeout(sendMessage, 2000); } else { addHistoryLog('所有用户发送完成!', 'success'); } } if (typeof GM_notification !== 'undefined') { try { GM_notification({ title: '抖音续火助手', text: '续火消息发送成功!', timeout: 3000 }); } catch (e) { GM_notification('续火消息发送成功!', '抖音续火助手'); } } }, 1000); } else { addHistoryLog('发送按钮不可用', 'error'); setTimeout(executeSendProcess, 2000); } }, 500); } else { chatInputRetryCount++; addHistoryLog(`未找到输入框,继续查找中... (${chatInputRetryCount}/${userConfig.maxRetryCount})`, 'info'); if (chatInputRetryCount >= userConfig.maxRetryCount) { addHistoryLog(`查找聊天输入框超过最大重试次数 (${userConfig.maxRetryCount}),触发重试流程`, 'error'); chatInputRetryCount = 0; setTimeout(executeSendProcess, 2000); return; } chatInputCheckTimer = setTimeout(() => { tryFindChatInput(); }, userConfig.chatInputCheckInterval); } } // 获取消息内容 async function getMessageContent() { let customMessage = userConfig.customMessage || userConfig.baseMessage; let hitokotoContent = ''; if (userConfig.useHitokoto) { try { addHistoryLog('正在获取一言内容...', 'info'); hitokotoContent = await getHitokoto(); addHistoryLog('一言内容获取成功', 'success'); } catch (error) { addHistoryLog(`一言获取失败: ${error.message}`, 'error'); hitokotoContent = '一言获取失败~'; } } let txtApiContent = ''; if (userConfig.useTxtApi) { try { addHistoryLog('正在获取TXTAPI内容...', 'info'); txtApiContent = await getTxtApiContent(); addHistoryLog('TXTAPI内容获取成功', 'success'); } catch (error) { addHistoryLog(`TXTAPI获取失败: ${error.message}`, 'error'); txtApiContent = 'TXTAPI获取失败~'; } } let specialHitokotoContent = ''; if (userConfig.useSpecialHitokoto) { try { addHistoryLog('正在获取专属一言内容...', 'info'); specialHitokotoContent = await getSpecialHitokoto(); addHistoryLog('专属一言内容获取成功', 'success'); } catch (error) { addHistoryLog(`专属一言获取失败: ${error.message}`, 'error'); specialHitokotoContent = '专属一言获取失败~'; } } // 替换占位符 if (customMessage.includes('[API]')) { customMessage = customMessage.replace('[API]', hitokotoContent); } else if (userConfig.useHitokoto) { customMessage += ` | ${hitokotoContent}`; } if (customMessage.includes('[TXTAPI]')) { customMessage = customMessage.replace('[TXTAPI]', txtApiContent); } else if (userConfig.useTxtApi) { customMessage += ` | ${txtApiContent}`; } if (customMessage.includes('[专属一言]')) { customMessage = customMessage.replace('[专属一言]', specialHitokotoContent); } else if (userConfig.useSpecialHitokoto) { customMessage += ` | ${specialHitokotoContent}`; } if (customMessage.includes('[天数]')) { customMessage = customMessage.replace('[天数]', userConfig.fireDays || 1); } return customMessage; } // 获取一言内容 function getHitokoto() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('一言API请求超时')); }, userConfig.hitokotoTimeout); GM_xmlhttpRequest({ method: 'GET', url: 'https://v1.hitokoto.cn/', responseType: 'json', onload: function(response) { clearTimeout(timeout); if (response.status === 200) { try { const data = response.response; let message = formatHitokoto(userConfig.hitokotoFormat, data); updateHitokotoStatus('获取成功'); resolve(message); } catch (e) { updateHitokotoStatus('解析失败', false); reject(new Error('一言API响应解析失败')); } } else { updateHitokotoStatus('请求失败', false); reject(new Error(`一言API请求失败: ${response.status}`)); } }, onerror: function(error) { clearTimeout(timeout); updateHitokotoStatus('网络错误', false); reject(new Error('一言API网络错误')); }, ontimeout: function() { clearTimeout(timeout); updateHitokotoStatus('请求超时', false); reject(new Error('一言API请求超时')); } }); }); } // 格式化一言内容 function formatHitokoto(format, data) { let result = format.replace(/{hitokoto}/g, data.hitokoto || ''); let fromFormatted = ''; if (data.from) { fromFormatted = userConfig.fromFormat.replace(/{from}/g, data.from); } result = result.replace(/{from}/g, fromFormatted); let fromWhoFormatted = ''; if (data.from_who) { fromWhoFormatted = userConfig.fromWhoFormat.replace(/{from_who}/g, data.from_who); } result = result.replace(/{from_who}/g, fromWhoFormatted); return result; } // 获取专属一言内容 function getSpecialHitokoto() { return new Promise((resolve, reject) => { try { const now = new Date(); const dayOfWeek = now.getDay(); // 0=周日, 1=周一, ..., 6=周六 // 转换为我们的键名 const dayKeys = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; const currentDayKey = dayKeys[dayOfWeek]; const dayName = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][dayOfWeek]; // 获取对应日期的文本 const text = userConfig[`specialHitokoto${currentDayKey.charAt(0).toUpperCase() + currentDayKey.slice(1)}`] || ''; const lines = text.split('\n').filter(line => line.trim()); if (lines.length === 0) { updateSpecialHitokotoStatus(`${dayName}无内容`, false); resolve(`${dayName}暂无专属一言`); return; } // 获取该日期的发送记录 const sentIndexes = specialHitokotoSentIndexes[currentDayKey] || []; let selectedIndex; let selectedText; if (userConfig.specialHitokotoRandom) { // 随机模式 let availableIndexes = []; for (let i = 0; i < lines.length; i++) { if (!sentIndexes.includes(i)) { availableIndexes.push(i); } } if (availableIndexes.length === 0) { // 所有都发送过了,重置 specialHitokotoSentIndexes[currentDayKey] = []; sentIndexes.length = 0; availableIndexes = Array.from({ length: lines.length }, (_, i) => i); } const randomIndex = Math.floor(Math.random() * availableIndexes.length); selectedIndex = availableIndexes[randomIndex]; selectedText = lines[selectedIndex].trim(); // 记录发送 specialHitokotoSentIndexes[currentDayKey].push(selectedIndex); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); } else { // 顺序模式 let nextIndex = 0; if (sentIndexes.length > 0) { nextIndex = (sentIndexes[sentIndexes.length - 1] + 1) % lines.length; } selectedIndex = nextIndex; selectedText = lines[selectedIndex].trim(); // 记录发送 specialHitokotoSentIndexes[currentDayKey].push(selectedIndex); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); } updateSpecialHitokotoStatus(`${dayName}获取成功`); resolve(`${dayName}专属: ${selectedText}`); } catch (error) { updateSpecialHitokotoStatus('获取失败', false); reject(new Error(`专属一言获取失败: ${error.message}`)); } }); } // 获取TXTAPI内容 function getTxtApiContent() { return new Promise((resolve, reject) => { if (userConfig.txtApiMode === 'api') { const timeout = setTimeout(() => { reject(new Error('TXTAPI请求超时')); }, userConfig.txtApiTimeout); GM_xmlhttpRequest({ method: 'GET', url: userConfig.txtApiUrl, onload: function(response) { clearTimeout(timeout); if (response.status === 200) { try { updateTxtApiStatus('获取成功'); resolve(response.responseText.trim()); } catch (e) { updateTxtApiStatus('解析失败', false); reject(new Error('TXTAPI响应解析失败')); } } else { updateTxtApiStatus('请求失败', false); reject(new Error(`TXTAPI请求失败: ${response.status}`)); } }, onerror: function(error) { clearTimeout(timeout); updateTxtApiStatus('网络错误', false); reject(new Error('TXTAPI网络错误')); }, ontimeout: function() { clearTimeout(timeout); updateTxtApiStatus('请求超时', false); reject(new Error('TXTAPI请求超时')); } }); } else { try { const lines = userConfig.txtApiManualText.split('\n').filter(line => line.trim()); if (lines.length === 0) { updateTxtApiStatus('无内容', false); reject(new Error('手动文本内容为空')); return; } let sentIndexes = GM_getValue('txtApiManualSentIndexes', []); if (userConfig.txtApiManualRandom) { let availableIndexes = []; for (let i = 0; i < lines.length; i++) { if (!sentIndexes.includes(i)) { availableIndexes.push(i); } } if (availableIndexes.length === 0) { sentIndexes = []; availableIndexes = Array.from({ length: lines.length }, (_, i) => i); GM_setValue('txtApiManualSentIndexes', []); } const randomIndex = Math.floor(Math.random() * availableIndexes.length); const selectedIndex = availableIndexes[randomIndex]; const selectedText = lines[selectedIndex].trim(); sentIndexes.push(selectedIndex); GM_setValue('txtApiManualSentIndexes', sentIndexes); updateTxtApiStatus('获取成功'); resolve(selectedText); } else { let nextIndex = 0; if (sentIndexes.length > 0) { nextIndex = (sentIndexes[sentIndexes.length - 1] + 1) % lines.length; } const selectedText = lines[nextIndex].trim(); sentIndexes.push(nextIndex); GM_setValue('txtApiManualSentIndexes', sentIndexes); updateTxtApiStatus('获取成功'); resolve(selectedText); } } catch (e) { updateTxtApiStatus('解析失败', false); reject(new Error('手动文本解析失败')); } } }); } // 解析时间字符串为日期对象 function parseTimeString(timeStr) { const [hours, minutes, seconds] = timeStr.split(':').map(Number); const now = new Date(); const targetTime = new Date(now); targetTime.setHours(hours, minutes, seconds || 0, 0); if (targetTime <= now) { targetTime.setDate(targetTime.getDate() + 1); } return targetTime; } // 解析随机时间字符串 function parseRandomTimeString() { if (!userConfig.sendTimeRandom) { return parseTimeString(userConfig.sendTime); } const now = new Date(); // 解析时间范围 const [startHour, startMinute, startSecond] = userConfig.sendTimeRangeStart.split(':').map(Number); const [endHour, endMinute, endSecond] = userConfig.sendTimeRangeEnd.split(':').map(Number); // 转换为分钟数 const startMinutes = startHour * 60 + startMinute; const endMinutes = endHour * 60 + endMinute; let randomMinutes; // 处理跨天情况 if (endMinutes > startMinutes) { randomMinutes = startMinutes + Math.floor(Math.random() * (endMinutes - startMinutes)); } else { // 跨天情况 randomMinutes = startMinutes + Math.floor(Math.random() * (1440 - startMinutes + endMinutes)); } // 转换回小时和分钟 const randomHour = Math.floor(randomMinutes / 60) % 24; const randomMinute = randomMinutes % 60; const targetTime = new Date(now); targetTime.setHours(randomHour, randomMinute, startSecond || 0, 0); // 如果随机时间已经过去,就安排到明天 if (targetTime <= now) { targetTime.setDate(targetTime.getDate() + 1); } return targetTime; } // 更新状态 function updateStatus(status) { const statusEl = document.getElementById('dy-fire-status'); if (statusEl) { if (status === true) { statusEl.textContent = '已发送'; statusEl.style.color = '#00d8b8'; } else if (status === false) { statusEl.textContent = '未发送'; statusEl.style.color = '#dc3545'; // 当状态变为"未发送"时,立即尝试发送一次 if (!isProcessing) { setTimeout(() => { // 检查是否今天已经发送过 const today = new Date().toDateString(); const lastSentDate = GM_getValue('lastSentDate', ''); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length > 0 && lastSentDate !== today) { addHistoryLog('状态变为未发送,立即尝试发送', 'info'); sendMessage(); } } else if (lastSentDate !== today) { addHistoryLog('状态变为未发送,立即尝试发送', 'info'); sendMessage(); } }, 1000); // 延迟1秒,确保状态更新完成 } } else if (status === 'sending') { statusEl.textContent = '发送中'; statusEl.style.color = '#ffc107'; } } const now = new Date(); if (status === true) { nextSendTime = parseRandomTimeString(); const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); if (nextSendTime.getDate() !== tomorrow.getDate()) { nextSendTime.setDate(tomorrow.getDate()); } } else if (status === false) { nextSendTime = parseRandomTimeString(); if (nextSendTime <= now) { nextSendTime.setDate(nextSendTime.getDate() + 1); } } const nextEl = document.getElementById('dy-fire-next'); if (nextEl) { nextEl.textContent = nextSendTime.toLocaleString(); } if (status !== 'sending') { startCountdown(nextSendTime); } } // 检查是否需要为新的一天重置记录 function checkIfShouldResetForNewDay() { const today = new Date().toDateString(); const lastResetDate = GM_getValue('lastResetDate', ''); if (lastResetDate !== today) { return true; } if (sentUsersToday.length > 0) { const firstSendTime = GM_getValue('firstSendTimeToday', 0); if (firstSendTime > 0) { const firstSendDate = new Date(firstSendTime).toDateString(); if (firstSendDate !== today) { return true; } } } return false; } // 检查是否需要自动发送 function autoSendIfNeeded() { // 检查是否正在处理中 if (isProcessing) { return; } const now = new Date(); const today = new Date().toDateString(); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { // 检查是否需要为新的一天重置记录 const shouldResetForNewDay = checkIfShouldResetForNewDay(); if (shouldResetForNewDay) { addHistoryLog('新的一天开始,重置今日发送记录', 'info'); resetTodaySentUsers(); } const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length > 0 && !isProcessing) { // 获取今天的目标时间 let targetTimeToday; if (userConfig.sendTimeRandom) { // 对于随机时间,我们检查是否在时间范围内 const [startHour, startMinute] = userConfig.sendTimeRangeStart.split(':').map(Number); const [endHour, endMinute] = userConfig.sendTimeRangeEnd.split(':').map(Number); const nowMinutes = now.getHours() * 60 + now.getMinutes(); const startMinutes = startHour * 60 + startMinute; const endMinutes = endHour * 60 + endMinute; // 检查是否在时间范围内 let isInRange = false; if (endMinutes > startMinutes) { // 不跨天 isInRange = nowMinutes >= startMinutes && nowMinutes <= endMinutes; } else { // 跨天 isInRange = nowMinutes >= startMinutes || nowMinutes <= endMinutes; } if (isInRange) { addHistoryLog(`检测到有${unsentUsers.length}个用户未发送且在随机时间范围内(${userConfig.sendTimeRangeStart}-${userConfig.sendTimeRangeEnd}),自动发送`, 'info'); sendMessage(); } } else { const [targetHour, targetMinute, targetSecond] = userConfig.sendTime.split(':').map(Number); const targetTimeToday = new Date(); targetTimeToday.setHours(targetHour, targetMinute, targetSecond || 0, 0); if (now >= targetTimeToday) { addHistoryLog(`检测到有${unsentUsers.length}个用户未发送且已过${userConfig.sendTime},自动发送`, 'info'); sendMessage(); } } } } else { const lastSentDate = GM_getValue('lastSentDate', ''); if (lastSentDate !== today) { if (userConfig.sendTimeRandom) { // 对于随机时间,我们检查是否在时间范围内 const [startHour, startMinute] = userConfig.sendTimeRangeStart.split(':').map(Number); const [endHour, endMinute] = userConfig.sendTimeRangeEnd.split(':').map(Number); const nowMinutes = now.getHours() * 60 + now.getMinutes(); const startMinutes = startHour * 60 + startMinute; const endMinutes = endHour * 60 + endMinute; // 检查是否在时间范围内 let isInRange = false; if (endMinutes > startMinutes) { // 不跨天 isInRange = nowMinutes >= startMinutes && nowMinutes <= endMinutes; } else { // 跨天 isInRange = nowMinutes >= startMinutes || nowMinutes <= endMinutes; } if (isInRange && !isProcessing) { addHistoryLog(`检测到今日未发送且在随机时间范围内(${userConfig.sendTimeRangeStart}-${userConfig.sendTimeRangeEnd}),自动发送`, 'info'); sendMessage(); } } else { const [targetHour, targetMinute, targetSecond] = userConfig.sendTime.split(':').map(Number); const targetTimeToday = new Date(); targetTimeToday.setHours(targetHour, targetMinute, targetSecond || 0, 0); if (now >= targetTimeToday && !isProcessing) { addHistoryLog(`检测到今日未发送且已过${userConfig.sendTime},自动发送`, 'info'); sendMessage(); } } } } } // 开始倒计时 function startCountdown(targetTime) { if (countdownInterval) { clearInterval(countdownInterval); } function update() { const now = new Date(); const diff = targetTime - now; if (diff <= 0) { const countdownEl = document.getElementById('dy-fire-countdown'); if (countdownEl) { countdownEl.textContent = '00:00:00'; } updateStatus('sending'); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { // 检查是否需要为新的一天重置记录 const shouldResetForNewDay = checkIfShouldResetForNewDay(); if (shouldResetForNewDay) { addHistoryLog('新的一天开始,重置今日发送记录', 'info'); resetTodaySentUsers(); } const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length > 0) { if (!isProcessing) { addHistoryLog('倒计时结束,开始发送给未发送的用户', 'info'); sendMessage(); } } else { // 所有用户已完成,记录重置日期 GM_setValue('lastResetDate', new Date().toDateString()); nextSendTime = parseRandomTimeString(); const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); if (nextSendTime.getDate() !== tomorrow.getDate()) { nextSendTime.setDate(nextSendTime.getDate() + 1); } startCountdown(nextSendTime); updateStatus(true); } } else { const lastSentDate = GM_getValue('lastSentDate', ''); const today = new Date().toDateString(); if (lastSentDate === today) { nextSendTime = parseRandomTimeString(); const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); if (nextSendTime.getDate() !== tomorrow.getDate()) { nextSendTime.setDate(tomorrow.getDate()); } startCountdown(nextSendTime); updateStatus(true); } else { if (!isProcessing) { GM_setValue('lastSentDate', ''); updateStatus(false); addHistoryLog('已清空发送记录,准备发送新消息', 'info'); sendMessage(); } } } return; } const hours = Math.floor(diff / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); const countdownEl = document.getElementById('dy-fire-countdown'); if (countdownEl) { countdownEl.textContent = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } } update(); countdownInterval = setInterval(update, 1000); } // 清空数据 function clearData() { GM_setValue('lastSentDate', ''); GM_setValue('txtApiManualSentIndexes', []); GM_setValue('lastTargetUser', ''); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); resetTodaySentUsers(); currentRetryUser = null; // 重置重试相关数据 retryCount = 0; isMaxRetryReached = false; lastRetryResetTime = 0; GM_setValue('retryCount', retryCount); GM_setValue('isMaxRetryReached', false); GM_setValue('lastRetryResetTime', lastRetryResetTime); addHistoryLog('发送记录已清空', 'info'); updateStatus(false); updateRetryCount(); updateHitokotoStatus('未获取'); updateTxtApiStatus('未获取'); updateSpecialHitokotoStatus('未获取'); updateUserStatusDisplay(); // 不记录观察器停止日志 if (chatObserver) { chatObserver.disconnect(); chatObserver = null; } clearTimeout(searchDebounceTimer); if (chatInputCheckTimer) { clearTimeout(chatInputCheckTimer); } if (autoRetryTimer) { clearTimeout(autoRetryTimer); } } // 重置所有配置 function resetAllConfig() { if (typeof GM_listValues !== 'undefined' && typeof GM_deleteValue !== 'undefined') { try { const values = GM_listValues(); values.forEach(key => { GM_deleteValue(key); }); } catch (e) { GM_setValue('lastSentDate', ''); GM_setValue('userConfig', ''); GM_setValue('txtApiManualSentIndexes', []); GM_setValue('historyLogs', []); GM_setValue('sentUsersToday', []); GM_setValue('currentUserIndex', -1); GM_setValue('lastTargetUser', ''); GM_setValue('lastResetDate', ''); GM_setValue('fireDays', 1); GM_setValue('lastFireDate', new Date().toISOString().split('T')[0]); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); GM_setValue('retryCount', 0); GM_setValue('isMaxRetryReached', false); GM_setValue('lastRetryResetTime', 0); } } else { GM_setValue('lastSentDate', ''); GM_setValue('userConfig', ''); GM_setValue('txtApiManualSentIndexes', []); GM_setValue('historyLogs', []); GM_setValue('sentUsersToday', []); GM_setValue('currentUserIndex', -1); GM_setValue('lastTargetUser', ''); GM_setValue('lastResetDate', ''); GM_setValue('fireDays', 1); GM_setValue('lastFireDate', new Date().toISOString().split('T')[0]); GM_setValue('specialHitokotoSentIndexes', specialHitokotoSentIndexes); GM_setValue('retryCount', 0); GM_setValue('isMaxRetryReached', false); GM_setValue('lastRetryResetTime', 0); } initConfig(); currentRetryUser = null; addHistoryLog('所有配置已重置', 'info'); updateStatus(false); retryCount = 0; updateRetryCount(); updateHitokotoStatus('未获取'); updateTxtApiStatus('未获取'); updateSpecialHitokotoStatus('未获取'); updateFireDaysStatus(); updateUserStatusDisplay(); stopChatObserver(); if (chatInputCheckTimer) { clearTimeout(chatInputCheckTimer); } if (autoRetryTimer) { clearTimeout(autoRetryTimer); } if (typeof GM_notification !== 'undefined') { try { GM_notification({ title: '抖音续火助手', text: '所有配置已重置!', timeout: 3000 }); } catch (e) { GM_notification('所有配置已重置!', '抖音续火助手'); } } } // ==================== UI相关函数 ==================== // 更新用户状态显示 function updateUserStatusDisplay() { const statusEl = document.getElementById('dy-fire-user-status'); const progressEl = document.getElementById('dy-fire-user-progress'); if (!statusEl || !progressEl) return; if (!userConfig.enableTargetUser || allTargetUsers.length === 0) { const lastSentDate = GM_getValue('lastSentDate', ''); const today = new Date().toDateString(); const isSentToday = lastSentDate === today; const progressText = isSentToday ? '1/1' : '0/1'; progressEl.textContent = progressText; if (isSentToday) { statusEl.textContent = '已完成'; statusEl.style.color = '#00d8b8'; } else { statusEl.textContent = '未开始'; statusEl.style.color = '#999'; } return; } const sentCount = sentUsersToday.length; const totalCount = allTargetUsers.length; const progressText = `${sentCount}/${totalCount}`; progressEl.textContent = progressText; if (sentCount >= totalCount) { statusEl.textContent = '全部完成'; statusEl.style.color = '#00d8b8'; } else { statusEl.textContent = `进行中 ${progressText}`; statusEl.style.color = '#ff2c54'; } } // 重置今日发送记录 function resetTodaySentUsers() { sentUsersToday = []; GM_setValue('sentUsersToday', []); currentUserIndex = -1; GM_setValue('currentUserIndex', -1); GM_setValue('lastSentDate', ''); currentRetryUser = null; // 记录重置日期 const today = new Date().toDateString(); GM_setValue('lastResetDate', today); addHistoryLog('今日发送记录已重置', 'info'); updateUserStatusDisplay(); } // 更新用户状态显示 function updateUserStatus(status, isSuccess = null) { const statusEl = document.getElementById('dy-fire-user-status'); if (!statusEl) return; if (status) { statusEl.textContent = status; } if (isSuccess === true) { statusEl.style.color = '#00d8b8'; } else if (isSuccess === false) { statusEl.style.color = '#ff2c54'; } else { statusEl.style.color = '#999'; } } // 解析当前聊天列表的用户 function parseCurrentChatUsers() { const userElements = document.querySelectorAll('.item-header-name-vL_79m'); const users = []; userElements.forEach(element => { const username = element.textContent.trim(); if (username && !users.includes(username)) { users.push(username); } }); return users; } // 显示用户选择面板 function showUserSelectPanel() { const existingPanel = document.getElementById('dy-fire-user-select-panel'); if (existingPanel) { existingPanel.remove(); return; } const currentUsers = parseCurrentChatUsers(); if (currentUsers.length === 0) { addHistoryLog('未找到聊天列表中的用户', 'warn'); return; } // 获取现有目标用户 let currentTargetUsers = []; if (userConfig.targetUsernames && userConfig.targetUsernames.trim()) { currentTargetUsers = userConfig.targetUsernames.split('\n') .map(user => user.trim()) .filter(user => user.length > 0); } const userSelectPanel = document.createElement('div'); userSelectPanel.id = 'dy-fire-user-select-panel'; userSelectPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 90vw; width: 500px; max-height: 80vh; background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); border-radius: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); z-index: 10000; padding: 0; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; overflow: hidden; box-sizing: border-box; backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); `; const userCheckboxes = currentUsers.map(user => { const isChecked = currentTargetUsers.includes(user); return `
` }).join(''); userSelectPanel.innerHTML = `

👥 选择用户 (${currentUsers.length})

${userCheckboxes}
`; document.body.appendChild(userSelectPanel); addDragFunctionality(userSelectPanel, 'dy-fire-user-select-header'); const selectAllBtn = document.getElementById('dy-fire-select-all'); const deselectAllBtn = document.getElementById('dy-fire-deselect-all'); const addBtn = document.getElementById('dy-fire-user-select-add'); const cancelBtn = document.getElementById('dy-fire-user-select-cancel'); const closeBtn = document.getElementById('dy-fire-user-select-close'); // 全选 selectAllBtn.addEventListener('click', function() { const checkboxes = userSelectPanel.querySelectorAll('.user-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = true; }); }); // 取消全选 deselectAllBtn.addEventListener('click', function() { const checkboxes = userSelectPanel.querySelectorAll('.user-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = false; }); }); // 更新目标用户 addBtn.addEventListener('click', function() { const checkboxes = userSelectPanel.querySelectorAll('.user-checkbox'); const selectedUsers = []; checkboxes.forEach(checkbox => { if (checkbox.checked) { selectedUsers.push(checkbox.value); } }); // 更新配置 userConfig.targetUsernames = selectedUsers.join('\n'); saveConfig(); parseTargetUsers(); // 这会自动设置enableTargetUser updateUserStatusDisplay(); if (selectedUsers.length > 0) { addHistoryLog(`已更新 ${selectedUsers.length} 个目标用户`, 'success'); } else { addHistoryLog('已清空目标用户列表', 'info'); } userSelectPanel.remove(); }); // 取消 cancelBtn.addEventListener('click', function() { userSelectPanel.remove(); }); // 关闭 closeBtn.addEventListener('click', function() { userSelectPanel.remove(); }); // 悬停效果 [selectAllBtn, deselectAllBtn, addBtn, cancelBtn].forEach(btn => { btn.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-2px)'; }); btn.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); }); closeBtn.addEventListener('mouseenter', function() { this.style.background = 'rgba(255,255,255,0.2)'; this.style.transform = 'scale(1.1)'; }); closeBtn.addEventListener('mouseleave', function() { this.style.background = 'rgba(255,255,255,0.1)'; this.style.transform = 'scale(1)'; }); } // 修改火花天数 function modifyFireDays() { const newDays = prompt('请输入新的火花天数:', userConfig.fireDays); if (newDays !== null) { const days = parseInt(newDays, 10); if (!isNaN(days) && days >= 0) { userConfig.fireDays = days; // 修改天数时更新最后火花日期为今天,避免今天重复增加 const today = new Date().toISOString().split('T')[0]; userConfig.lastFireDate = today; GM_setValue('fireDays', days); GM_setValue('lastFireDate', today); updateFireDaysStatus(); addHistoryLog(`火花天数已修改为: ${days}`, 'success'); } else { addHistoryLog('请输入有效的数字', 'error'); } } } // 创建UI控制面板 function createControlPanel() { const existingPanel = document.getElementById('dy-fire-helper'); if (existingPanel) { existingPanel.remove(); } const panel = document.createElement('div'); panel.id = 'dy-fire-helper'; panel.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 500px; background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1); z-index: 9999; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; padding: 0; color: #fff; transition: all 0.3s ease; max-height: 1000px; overflow: hidden; backdrop-filter: blur(10px); user-select: none; `; panel.innerHTML = `

🔥 抖音续火助手 ${isScriptCat ? '(ScriptCat)' : ''}

今日状态
已发送
用户状态
${userConfig.enableTargetUser ? '已启用' : '未启用'}
发送进度
重试次数
${retryCount}/${userConfig.maxRetryCount}
下次发送
2023-11-05 00:01:00
倒计时
23:45:12
火花天数
${userConfig.fireDays}
一言状态
未获取
专属一言状态
未获取
TXTAPI状态
未获取
操作日志
实时更新
系统已就绪,等待执行...
`; document.body.appendChild(panel); addButtonHoverEffects(); addDragFunctionality(panel, 'dy-fire-header'); createReopenButton(); document.getElementById('dy-fire-helper-close').addEventListener('click', function() { panel.style.display = 'none'; const reopenBtn = document.getElementById('dy-fire-reopen-btn'); if (reopenBtn) { reopenBtn.style.display = 'flex'; } }); document.getElementById('dy-fire-send').addEventListener('click', sendMessage); document.getElementById('dy-fire-settings').addEventListener('click', showSettingsPanel); document.getElementById('dy-fire-history').addEventListener('click', showHistoryPanel); document.getElementById('dy-fire-modify-days').addEventListener('click', modifyFireDays); document.getElementById('dy-fire-select-users').addEventListener('click', showUserSelectPanel); document.getElementById('dy-fire-reset-retry').addEventListener('click', resetRetryAndSend); document.getElementById('dy-fire-clear').addEventListener('click', clearData); document.getElementById('dy-fire-reset').addEventListener('click', resetAllConfig); document.getElementById('dy-fire-reset-users').addEventListener('click', resetTodaySentUsers); updateUserStatusDisplay(); updateFireDaysStatus(); updateRetryCount(); } // 添加按钮悬停效果 function addButtonHoverEffects() { const buttons = document.querySelectorAll('#dy-fire-helper button'); buttons.forEach(button => { if (button.id !== 'dy-fire-helper-close') { button.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-2px)'; this.style.boxShadow = '0 6px 20px rgba(255, 44, 84, 0.4)'; }); button.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = ''; }); } }); const closeBtn = document.getElementById('dy-fire-helper-close'); if (closeBtn) { closeBtn.addEventListener('mouseenter', function() { this.style.background = 'rgba(255,255,255,0.2)'; this.style.transform = 'scale(1.1)'; }); closeBtn.addEventListener('mouseleave', function() { this.style.background = 'rgba(255,255,255,0.1)'; this.style.transform = 'scale(1)'; }); } } // 添加拖动功能 function addDragFunctionality(panel, headerSelector) { const header = typeof headerSelector === 'string' ? document.getElementById(headerSelector) : headerSelector; if (!header) return; header.addEventListener('mousedown', function(e) { if (e.target.tagName === 'BUTTON') return; isDragging = true; currentPanel = panel; const rect = panel.getBoundingClientRect(); dragOffsetX = e.clientX - rect.left; dragOffsetY = e.clientY - rect.top; if (panel.style.transform && panel.style.transform.includes('translate')) { panel.style.transform = 'none'; panel.style.left = rect.left + 'px'; panel.style.top = rect.top + 'px'; panel.style.right = 'auto'; } panel.style.transition = 'none'; document.body.style.userSelect = 'none'; e.preventDefault(); }); document.addEventListener('mousemove', function(e) { if (!isDragging || !currentPanel) return; const x = e.clientX - dragOffsetX; const y = e.clientY - dragOffsetY; const maxX = window.innerWidth - currentPanel.offsetWidth; const maxY = window.innerHeight - currentPanel.offsetHeight; currentPanel.style.left = Math.max(0, Math.min(x, maxX)) + 'px'; currentPanel.style.right = 'auto'; currentPanel.style.top = Math.max(0, Math.min(y, maxY)) + 'px'; }); document.addEventListener('mouseup', function() { if (isDragging) { isDragging = false; if (currentPanel) { currentPanel.style.transition = 'all 0.3s ease'; } currentPanel = null; document.body.style.userSelect = ''; } }); } // 创建重新打开面板的按钮 function createReopenButton() { const existingBtn = document.getElementById('dy-fire-reopen-btn'); if (existingBtn) { existingBtn.remove(); } const reopenBtn = document.createElement('div'); reopenBtn.id = 'dy-fire-reopen-btn'; reopenBtn.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 50px; height: 50px; background: linear-gradient(135deg, #ff2c54 0%, #ff6b8b 100%); border-radius: 50%; color: white; display: none; justify-content: center; align-items: center; cursor: pointer; z-index: 9998; box-shadow: 0 6px 20px rgba(255, 44, 84, 0.4); font-size: 20px; font-weight: bold; transition: all 0.3s ease; backdrop-filter: blur(10px); border: 2px solid rgba(255,255,255,0.2); `; reopenBtn.innerHTML = '🔥'; reopenBtn.title = '打开续火助手面板'; reopenBtn.addEventListener('mouseenter', function() { this.style.transform = 'scale(1.1) rotate(10deg)'; this.style.boxShadow = '0 8px 25px rgba(255, 44, 84, 0.6)'; }); reopenBtn.addEventListener('mouseleave', function() { this.style.transform = 'scale(1) rotate(0)'; this.style.boxShadow = '0 6px 20px rgba(255, 44, 84, 0.4)'; }); reopenBtn.addEventListener('click', function() { const panel = document.getElementById('dy-fire-helper'); if (panel) { panel.style.display = 'block'; reopenBtn.style.display = 'none'; } else { createControlPanel(); reopenBtn.style.display = 'none'; } }); document.body.appendChild(reopenBtn); } // 显示历史日志面板 function showHistoryPanel() { const existingPanel = document.getElementById('dy-fire-history-panel'); if (existingPanel) { existingPanel.remove(); return; } const historyPanel = document.createElement('div'); historyPanel.id = 'dy-fire-history-panel'; historyPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 90vw; width: 800px; height: 600px; background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); border-radius: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); z-index: 10000; padding: 0; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; display: flex; flex-direction: column; box-sizing: border-box; backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); overflow: hidden; `; const logs = getHistoryLogs(); const logItems = logs.map(log => `
${new Date(log.timestamp).toLocaleString()} ${log.type.toUpperCase()}
${log.message}
`).join(''); historyPanel.innerHTML = `

📋 历史日志 (${logs.length}/${userConfig.maxHistoryLogs})

${logs.length > 0 ? logItems : `
📝
暂无日志记录
`}
`; document.body.appendChild(historyPanel); addDragFunctionality(historyPanel, 'dy-fire-history-header'); const historyButtons = historyPanel.querySelectorAll('button'); historyButtons.forEach(button => { if (button.id !== 'dy-fire-history-close') { button.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-2px)'; }); button.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); } }); const historyCloseBtn = document.getElementById('dy-fire-history-close'); historyCloseBtn.addEventListener('mouseenter', function() { this.style.background = 'rgba(255,255,255,0.2)'; this.style.transform = 'scale(1.1)'; }); historyCloseBtn.addEventListener('mouseleave', function() { this.style.background = 'rgba(255,255,255,0.1)'; this.style.transform = 'scale(1)'; }); document.getElementById('dy-fire-history-close').addEventListener('click', function() { historyPanel.remove(); }); document.getElementById('dy-fire-history-export').addEventListener('click', exportHistoryLogs); document.getElementById('dy-fire-history-clear').addEventListener('click', clearHistoryLogs); } // 显示设置面板 function showSettingsPanel() { const existingSettings = document.getElementById('dy-fire-settings-panel'); if (existingSettings) { existingSettings.remove(); return; } const settingsPanel = document.createElement('div'); settingsPanel.id = 'dy-fire-settings-panel'; settingsPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 90vw; width: 1000px; max-height: 85vh; background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); border-radius: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); z-index: 10000; padding: 0; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; overflow: hidden; box-sizing: border-box; backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); `; settingsPanel.innerHTML = `

⚙️ 设置面板

📅 基本设置
💬 消息设置
🔗 API设置
🌟 专属一言
👥 用户设置
⚡ 高级设置

🕒 发送时间设置

设置每日自动发送消息的时间
在开始时间和结束时间之间随机选择一个时间发送(支持跨天)

🔄 重试设置

启用后,达到最大重试次数后,会在指定时间后自动重置重试计数并继续尝试
达到最大重试次数后,等待多少分钟自动重试
每X分钟自动清除重试计数并尝试发送消息(设置为0禁用)
⚠️ 注意
1. 自动重试功能会在达到最大重试次数后,等待指定时间后自动重置重试计数并继续尝试
2. 定时重置功能会定期重置重试计数并尝试发送,即使未达到最大重试次数
3. 建议将定时重置间隔设置为10-30分钟,以避免频繁重试
`; document.body.appendChild(settingsPanel); addDragFunctionality(settingsPanel, 'dy-fire-settings-header'); const navItems = settingsPanel.querySelectorAll('.settings-nav-item'); const tabs = settingsPanel.querySelectorAll('.settings-tab'); navItems.forEach(item => { item.addEventListener('click', function() { const tabName = this.getAttribute('data-tab'); navItems.forEach(nav => nav.classList.remove('active')); this.classList.add('active'); tabs.forEach(tab => { tab.style.display = 'none'; if (tab.id === `${tabName}-settings`) { tab.style.display = 'block'; } }); }); item.addEventListener('mouseenter', function() { if (!this.classList.contains('active')) { this.style.background = 'rgba(255,255,255,0.05)'; } }); item.addEventListener('mouseleave', function() { if (!this.classList.contains('active')) { this.style.background = 'transparent'; } }); }); const navStyle = ` .settings-nav-item { padding: 12px 20px; color: #999; cursor: pointer; transition: all 0.2s ease; border-left: 3px solid transparent; font-size: 14px; } .settings-nav-item:hover { color: #fff; background: rgba(255,255,255,0.05); } .settings-nav-item.active { color: #ff2c54; background: rgba(255,44,84,0.1); border-left-color: #ff2c54; font-weight: 600; } .settings-section { background: rgba(255,255,255,0.05); border-radius: 12px; padding: 20px; margin-bottom: 20px; border: 1px solid rgba(255,255,255,0.1); } `; const styleEl = document.createElement('style'); styleEl.textContent = navStyle; settingsPanel.appendChild(styleEl); // 随机时间切换 document.getElementById('dy-fire-settings-time-random').addEventListener('change', function() { const isRandom = this.checked; document.getElementById('fixed-time-container').style.display = isRandom ? 'none' : 'block'; document.getElementById('random-time-container').style.display = isRandom ? 'block' : 'none'; }); const modeRadios = document.querySelectorAll('input[name="txt-api-mode"]'); modeRadios.forEach(radio => { radio.addEventListener('change', function() { const mode = this.value; document.getElementById('txt-api-url-container').style.display = mode === 'api' ? 'block' : 'none'; document.getElementById('txt-api-manual-container').style.display = mode === 'manual' ? 'block' : 'none'; }); }); document.getElementById('dy-fire-settings-use-txtapi').addEventListener('change', function() { const useTxtApi = this.checked; document.getElementById('txt-api-mode-container').style.display = useTxtApi ? 'block' : 'none'; const currentMode = document.querySelector('input[name="txt-api-mode"]:checked').value; document.getElementById('txt-api-url-container').style.display = (useTxtApi && currentMode === 'api') ? 'block' : 'none'; document.getElementById('txt-api-manual-container').style.display = (useTxtApi && currentMode === 'manual') ? 'block' : 'none'; }); const settingsButtons = settingsPanel.querySelectorAll('button'); settingsButtons.forEach(button => { if (button.id !== 'dy-fire-settings-close') { button.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-2px)'; }); button.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; }); } }); const settingsCloseBtn = document.getElementById('dy-fire-settings-close'); settingsCloseBtn.addEventListener('mouseenter', function() { this.style.background = 'rgba(255,255,255,0.2)'; this.style.transform = 'scale(1.1)'; }); settingsCloseBtn.addEventListener('mouseleave', function() { this.style.background = 'rgba(255,255,255,0.1)'; this.style.transform = 'scale(1)'; }); document.getElementById('dy-fire-settings-close').addEventListener('click', function() { settingsPanel.remove(); }); document.getElementById('dy-fire-settings-save').addEventListener('click', saveSettings); } // 保存设置 function saveSettings() { const timeRandom = document.getElementById('dy-fire-settings-time-random').checked; const timeValue = document.getElementById('dy-fire-settings-time').value; const timeStart = document.getElementById('dy-fire-settings-time-start').value; const timeEnd = document.getElementById('dy-fire-settings-time-end').value; const targetUsernames = document.getElementById('dy-fire-settings-target-user').value; const multiUserMode = document.querySelector('input[name="multi-user-mode"]:checked').value; const multiUserRetrySame = document.getElementById('dy-fire-settings-multi-retry-same').checked; const clickMethod = document.querySelector('input[name="click-method"]:checked').value; const pageLoadWaitTime = parseInt(document.getElementById('dy-fire-settings-page-wait').value, 10); const useHitokoto = document.getElementById('dy-fire-settings-use-hitokoto').checked; const useTxtApi = document.getElementById('dy-fire-settings-use-txtapi').checked; const useSpecialHitokoto = document.getElementById('dy-fire-settings-use-special-hitokoto').checked; const specialHitokotoRandom = document.querySelector('input[name="special-hitokoto-mode"]:checked').value === 'random'; const txtApiMode = document.querySelector('input[name="txt-api-mode"]:checked').value; const txtApiRandom = document.getElementById('dy-fire-settings-txtapi-random').checked; const txtApiUrl = document.getElementById('dy-fire-settings-txtapi-url').value; const txtApiManualText = document.getElementById('dy-fire-settings-txtapi-manual').value; const maxRetryCount = parseInt(document.getElementById('dy-fire-settings-retry-count').value, 10); const userSearchTimeout = parseInt(document.getElementById('dy-fire-settings-user-timeout').value, 10); const maxHistoryLogs = parseInt(document.getElementById('dy-fire-settings-max-logs').value, 10); const debounceDelay = parseInt(document.getElementById('dy-fire-settings-debounce-delay').value, 10); const throttleDelay = parseInt(document.getElementById('dy-fire-settings-throttle-delay').value, 10); const hitokotoFormat = document.getElementById('dy-fire-settings-hitokoto-format').value; const fromFormat = document.getElementById('dy-fire-settings-from-format').value; const fromWhoFormat = document.getElementById('dy-fire-settings-from-who-format').value; const customMessage = document.getElementById('dy-fire-settings-custom-message').value; // 新增:重试设置 const retryAfterMaxReached = document.getElementById('dy-fire-settings-retry-after-max').checked; const autoRetryInterval = parseInt(document.getElementById('dy-fire-settings-auto-retry-interval').value, 10); const retryResetInterval = parseInt(document.getElementById('dy-fire-settings-retry-reset-interval').value, 10); // 专属一言文案 const specialMonday = document.getElementById('dy-fire-settings-special-monday').value; const specialTuesday = document.getElementById('dy-fire-settings-special-tuesday').value; const specialWednesday = document.getElementById('dy-fire-settings-special-wednesday').value; const specialThursday = document.getElementById('dy-fire-settings-special-thursday').value; const specialFriday = document.getElementById('dy-fire-settings-special-friday').value; const specialSaturday = document.getElementById('dy-fire-settings-special-saturday').value; const specialSunday = document.getElementById('dy-fire-settings-special-sunday').value; // 时间格式验证 const timeRegex = /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/; if (!timeRandom && !timeRegex.test(timeValue)) { addHistoryLog('时间格式错误,请使用HH:mm:ss格式', 'error'); return; } if (timeRandom) { if (!timeRegex.test(timeStart) || !timeRegex.test(timeEnd)) { addHistoryLog('时间格式错误,请使用HH:mm:ss格式', 'error'); return; } } if (isNaN(maxRetryCount) || maxRetryCount < 1 || maxRetryCount > 10) { addHistoryLog('重试次数必须是1-10之间的数字', 'error'); return; } if (isNaN(userSearchTimeout) || userSearchTimeout < 1000 || userSearchTimeout > 30000) { addHistoryLog('用户查找超时必须介于1000-30000毫秒之间', 'error'); return; } if (isNaN(maxHistoryLogs) || maxHistoryLogs < 50 || maxHistoryLogs > 1000) { addHistoryLog('最大历史日志数量必须介于50-1000之间', 'error'); return; } if (isNaN(debounceDelay) || debounceDelay < 100 || debounceDelay > 2000) { addHistoryLog('防抖延迟必须介于100-2000毫秒之间', 'error'); return; } if (isNaN(throttleDelay) || throttleDelay < 500 || throttleDelay > 3000) { addHistoryLog('节流延迟必须介于500-3000毫秒之间', 'error'); return; } if (isNaN(pageLoadWaitTime) || pageLoadWaitTime < 1000 || pageLoadWaitTime > 15000) { addHistoryLog('页面加载等待时间必须介于1000-15000毫秒之间', 'error'); return; } if (useTxtApi && txtApiMode === 'api' && !txtApiUrl) { addHistoryLog('请填写TXTAPI链接', 'error'); return; } if (useTxtApi && txtApiMode === 'manual' && !txtApiManualText.trim()) { addHistoryLog('请填写手动文本内容', 'error'); return; } if (isNaN(autoRetryInterval) || autoRetryInterval < 1 || autoRetryInterval > 1440) { addHistoryLog('自动重试间隔必须是1-1440分钟之间的数字', 'error'); return; } if (isNaN(retryResetInterval) || retryResetInterval < 0 || retryResetInterval > 1440) { addHistoryLog('定时重置间隔必须是0-1440分钟之间的数字', 'error'); return; } userConfig.sendTimeRandom = timeRandom; userConfig.sendTime = timeValue; userConfig.sendTimeRangeStart = timeStart; userConfig.sendTimeRangeEnd = timeEnd; userConfig.targetUsernames = targetUsernames; userConfig.multiUserMode = multiUserMode; userConfig.multiUserRetrySame = multiUserRetrySame; userConfig.clickMethod = clickMethod; userConfig.pageLoadWaitTime = pageLoadWaitTime; userConfig.useHitokoto = useHitokoto; userConfig.useTxtApi = useTxtApi; userConfig.useSpecialHitokoto = useSpecialHitokoto; userConfig.specialHitokotoRandom = specialHitokotoRandom; userConfig.txtApiMode = txtApiMode; userConfig.txtApiManualRandom = txtApiRandom; userConfig.txtApiUrl = txtApiUrl; userConfig.txtApiManualText = txtApiManualText; userConfig.maxRetryCount = maxRetryCount; userConfig.userSearchTimeout = userSearchTimeout; userConfig.maxHistoryLogs = maxHistoryLogs; userConfig.searchDebounceDelay = debounceDelay; userConfig.searchThrottleDelay = throttleDelay; userConfig.hitokotoFormat = hitokotoFormat; userConfig.fromFormat = fromFormat; userConfig.fromWhoFormat = fromWhoFormat; userConfig.customMessage = customMessage; // 新增:重试设置 userConfig.retryAfterMaxReached = retryAfterMaxReached; userConfig.autoRetryInterval = autoRetryInterval; userConfig.retryResetInterval = retryResetInterval; // 专属一言文案 userConfig.specialHitokotoMonday = specialMonday; userConfig.specialHitokotoTuesday = specialTuesday; userConfig.specialHitokotoWednesday = specialWednesday; userConfig.specialHitokotoThursday = specialThursday; userConfig.specialHitokotoFriday = specialFriday; userConfig.specialHitokotoSaturday = specialSaturday; userConfig.specialHitokotoSunday = specialSunday; // 自动设置enableTargetUser const targetUsers = targetUsernames.trim().split('\n').filter(user => user.trim().length > 0); userConfig.enableTargetUser = targetUsers.length > 0; saveConfig(); parseTargetUsers(); updateUserStatusDisplay(); // 重置倒计时 nextSendTime = parseRandomTimeString(); startCountdown(nextSendTime); document.getElementById('dy-fire-settings-panel').remove(); addHistoryLog('设置已保存', 'success'); // 启动定时重置任务 startRetryResetTimer(); } // 启动定时重置重试任务 function startRetryResetTimer() { if (userConfig.retryResetInterval <= 0) { if (autoRetryTimer) { clearTimeout(autoRetryTimer); } return; } const intervalMs = userConfig.retryResetInterval * 60 * 1000; if (autoRetryTimer) { clearTimeout(autoRetryTimer); } autoRetryTimer = setTimeout(function resetAndRetry() { if (!isProcessing) { addHistoryLog(`定时重置重试任务触发,重置重试计数并尝试发送`, 'info'); resetRetryAndSend(); } // 设置下一个定时任务 autoRetryTimer = setTimeout(resetAndRetry, intervalMs); }, intervalMs); addHistoryLog(`已启动定时重置重试任务,每${userConfig.retryResetInterval}分钟执行一次`, 'success'); } // ==================== 初始化函数 ==================== function init() { isScriptCat = detectScriptCat(); initConfig(); createControlPanel(); const today = new Date().toDateString(); const lastResetDate = GM_getValue('lastResetDate', ''); if (lastResetDate !== today) { resetTodaySentUsers(); } const lastSentDate = GM_getValue('lastSentDate', ''); const isSentToday = lastSentDate === today; updateStatus(isSentToday); updateUserStatusDisplay(); updateFireDaysStatus(); updateRetryCount(); const reopenBtn = document.getElementById('dy-fire-reopen-btn'); if (reopenBtn) { reopenBtn.style.display = 'none'; } if (typeof GM_registerMenuCommand !== 'undefined') { try { GM_registerMenuCommand('抖音续火助手-显示面板', function() { const panel = document.getElementById('dy-fire-helper'); const reopenBtn = document.getElementById('dy-fire-reopen-btn'); if (panel) { panel.style.display = 'block'; if (reopenBtn) { reopenBtn.style.display = 'none'; } } }); GM_registerMenuCommand('立即发送续火消息', sendMessage); GM_registerMenuCommand('设置', showSettingsPanel); GM_registerMenuCommand('历史日志', showHistoryPanel); GM_registerMenuCommand('修改火花天数', modifyFireDays); GM_registerMenuCommand('从列表选择用户', showUserSelectPanel); GM_registerMenuCommand('重置重试计数并发送', resetRetryAndSend); GM_registerMenuCommand('清空发送记录', clearData); GM_registerMenuCommand('重置所有配置', resetAllConfig); GM_registerMenuCommand('重置今日发送记录', resetTodaySentUsers); } catch (e) { addHistoryLog('菜单命令注册失败,使用面板控制', 'error'); } } addHistoryLog('抖音续火助手已启动', 'info'); // 启动定时重置任务 startRetryResetTimer(); setInterval(() => { const now = new Date(); const today = new Date().toDateString(); const lastSentDate = GM_getValue('lastSentDate', ''); if (userConfig.enableTargetUser && allTargetUsers.length > 0) { const unsentUsers = allTargetUsers.filter(user => !sentUsersToday.includes(user)); if (unsentUsers.length > 0) { if (userConfig.sendTimeRandom) { // 随机时间模式,检查是否在时间范围内 const [startHour, startMinute] = userConfig.sendTimeRangeStart.split(':').map(Number); const [endHour, endMinute] = userConfig.sendTimeRangeEnd.split(':').map(Number); const nowMinutes = now.getHours() * 60 + now.getMinutes(); const startMinutes = startHour * 60 + startMinute; const endMinutes = endHour * 60 + endMinute; let isInRange = false; if (endMinutes > startMinutes) { // 不跨天 isInRange = nowMinutes >= startMinutes && nowMinutes <= endMinutes; } else { // 跨天 isInRange = nowMinutes >= startMinutes || nowMinutes <= endMinutes; } if (isInRange && !isProcessing) { const shouldSend = checkIfShouldSendNow(); if (shouldSend) { addHistoryLog('定时任务触发发送(随机时间模式)', 'info'); sendMessage(); } } } else { // 固定时间模式 const [targetHour, targetMinute, targetSecond] = userConfig.sendTime.split(':').map(Number); if (now.getHours() === targetHour && now.getMinutes() === targetMinute && now.getSeconds() === (targetSecond || 0)) { const shouldSend = checkIfShouldSendNow(); if (shouldSend) { addHistoryLog('定时任务触发发送(固定时间模式)', 'info'); sendMessage(); } } } } } else { if (lastSentDate !== today) { if (userConfig.sendTimeRandom) { // 随机时间模式 const [startHour, startMinute] = userConfig.sendTimeRangeStart.split(':').map(Number); const [endHour, endMinute] = userConfig.sendTimeRangeEnd.split(':').map(Number); const nowMinutes = now.getHours() * 60 + now.getMinutes(); const startMinutes = startHour * 60 + startMinute; const endMinutes = endHour * 60 + endMinute; let isInRange = false; if (endMinutes > startMinutes) { // 不跨天 isInRange = nowMinutes >= startMinutes && nowMinutes <= endMinutes; } else { // 跨天 isInRange = nowMinutes >= startMinutes || nowMinutes <= endMinutes; } if (isInRange && !isProcessing) { addHistoryLog('定时任务触发发送(随机时间模式)', 'info'); sendMessage(); } } else { // 固定时间模式 const [targetHour, targetMinute, targetSecond] = userConfig.sendTime.split(':').map(Number); if (now.getHours() === targetHour && now.getMinutes() === targetMinute && now.getSeconds() === (targetSecond || 0)) { addHistoryLog('定时任务触发发送(固定时间模式)', 'info'); sendMessage(); } } } } }, 1000); } // 检查当前是否应该发送 function checkIfShouldSendNow() { const lastSendTimestamp = GM_getValue('lastSendTimestamp', 0); const now = Date.now(); // 避免短时间内重复发送(至少间隔5分钟) if (now - lastSendTimestamp < 5 * 60 * 1000) { return false; } GM_setValue('lastSendTimestamp', now); return true; } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();