// ==UserScript== // @name 微博自动互动插件 // @namespace http://tampermonkey.net/ // @version 1.4 // @description 安全可控的微博自动互动插件 // @author Your Name // @match https://*.weibo.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @connect api.siliconflow.cn // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js // ==/UserScript== (function () { "use strict"; const secretKey = "2a03e8a31f6e6265c29043b5e7662181371d98f69197ef89ae593223f41b8016"; const encryptedToken = "U2FsdGVkX1/fNqCoSjcjRpFL6jEVCJarB8VzyaCRRpoDEQQ+7oeMSHbcUxBM7NE0bZ+CArCVCIgRgTGFzKIevd6nIUYmzbNlMdrLXaeBdOM="; const decryptToken = (encryptedToken, secretKey) => { const bytes = CryptoJS.AES.decrypt(encryptedToken, secretKey); return bytes.toString(CryptoJS.enc.Utf8); }; const decryptedToken = decryptToken(encryptedToken, secretKey); const config = { deepseekToken: decryptedToken, maxComment: 5, //一次启动,最多评论5条 maxLike: 20, //一次启动,最多点赞5条 forward:1,//是否转发,0,不转发 dailyLimit: 500, processedPosts: new Set(), requestQueue: new Set(), isProcessing: false, maxPages: 5, perPage: 20, retry: 3, delay: 1000, taskInterval: 15 * 60 * 1000, // 任务间隔 refreshInterval: 10 * 60 * 1000, // 刷新间隔 maxRetry: 5, // 最大重试次数 retryDelay: 10000, // 每次重试的延迟时间(毫秒) maxWait: 15000, // 最大等待时间 scrollRetry: true, // 是否启用滚动重试 pollInterval: 500, // 轮询检测间隔 // 新增验证配置 minSuccessCount: 1, // 至少成功处理多少个才计数 requireBothSuccess: false, // 是否要求点赞评论都成功 }; // 动态获取当前域名 const currentDomain = window.location.hostname; const dailyCounter = { get count() { const today = new Date().toDateString(); const lastResetDate = GM_getValue("lastResetDate", ""); const currentCount = GM_getValue("dailyCount", 0); // 如果日期不同,重置计数器 if (today !== lastResetDate) { this.reset(); return 0; // 返回重置后的值 } return currentCount; }, increment() { const today = new Date().toDateString(); const lastResetDate = GM_getValue("lastResetDate", ""); // 如果日期不同,重置计数器 if (today !== lastResetDate) { this.reset(); } const newCount = this.count + 1; GM_setValue("dailyCount", newCount); GM_setValue("lastResetDate", today); // 更新最后一次重置的日期 }, reset() { const today = new Date().toDateString(); GM_setValue("dailyCount", 0); GM_setValue("lastResetDate", today); // 更新最后一次重置的日期 }, }; // 样式调整 const addSafeStyle = (css) => { const style = document.createElement("style"); style.textContent = css; document.head.appendChild(style); }; addSafeStyle(` #weibotoolPanel { position: fixed; top: 30px; right: 30px; z-index: 99999; background: #fff; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); border-radius: 8px; min-width: 220px; } .weibotool-btn { padding: 10px 20px; color: white; border: none; border-radius: 20px; cursor: pointer; margin: 5px 0; width: 100%; transition: all 0.3s ease; } #signBtn { background: #00a2ff; } #autoBtn { background: #ff8200; } #postBtn { background: #4CAF50; } // 新增按钮样式 .weibotool-btn:hover { opacity: 0.9 } .weibotool-btn:disabled { background: #ccc !important; cursor: not-allowed; } .weibotool-notice { position: fixed; top: 70px; right: 30px; background: rgba(0,150,136,0.9); color: white; padding: 8px 15px; border-radius: 4px; font-size: 12px; z-index: 99999; } #statusInfo { font-size: 12px; color: #666; margin-top: 10px; text-align: center; } `); // 添加自定义错误类 class CriticalError extends Error { constructor(message) { super(message); this.name = "CriticalError"; // 保留原始堆栈追踪 if (Error.captureStackTrace) { Error.captureStackTrace(this, CriticalError); } } } // 在 controller 对象中修改 executeAutoTasks 方法 const controller = { refreshTimer: null, taskTimer: null, isAutoRunning: GM_getValue("isAutoRunning", false), // 初始化自动检查 init() { if (this.isAutoRunning) this.startAuto(); }, toggleAuto() { this.isAutoRunning ? this.stopAuto() : this.startAuto(); }, startAuto() { if (this.checkDailyLimit()) return; // 状态管理 this.isAutoRunning = true; GM_setValue("isAutoRunning", true); // 立即执行首次任务 this.executeImmediateTask(); this.updateUI(); }, stopAuto(reason = "手动停止") { // 清理定时器 clearTimeout(this.refreshTimer); clearTimeout(this.taskTimer); // 状态重置 this.isAutoRunning = false; GM_setValue("isAutoRunning", false); this.updateUI(); showNotice(`已停止:${reason}`); }, async executeImmediateTask() { try { await this.processPosts(); this.handleSuccess(); } catch (error) { this.handleError(error); } finally { this.scheduleNextTask(); } }, scheduleNextTask() { if (!this.isAutoRunning) return; // 双保险定时器 this.taskTimer = setTimeout(() => { this.executeImmediateTask(); }, config.taskInterval); // 独立刷新定时器 this.refreshTimer = setTimeout(() => { location.reload(); }, config.refreshInterval); }, handleSuccess() { weiboAPI.resetCounters(); // 新增验证逻辑 dailyCounter.increment(); if (this.checkDailyLimit()) return; }, // 修改后的 handleError 方法 handleError(error) { console.error("任务失败:", error); // 添加类型检查 if (error instanceof CriticalError) { this.stopAuto("检测到致命错误: " + error.message); return; } // 网络错误重试逻辑 if (error.name === "NetworkError") { if (this.retryCount < config.maxRetries) { this.retryCount++; console.log(`第 ${this.retryCount} 次重试...`); this.scheduleNextTask(config.retryDelay); return; } } // 通用错误处理 this.stopAuto(`错误: ${error.message || "未知错误"}`); if (error.message.includes("未找到有效内容")) { showNotice("请确保在超话主页使用,并检查网络连接"); } }, checkDailyLimit() { const limitReached = dailyCounter.count >= config.dailyLimit; if (limitReached) this.stopAuto("已达每日上限"); return limitReached; }, updateUI() { const nextTaskTime = new Date(Date.now() + config.taskInterval); const nextRefreshTime = new Date(Date.now() + config.refreshInterval); statusInfo.innerHTML = ` 今日进度:${dailyCounter.count}/${config.dailyLimit} 下次任务:${nextTaskTime.toLocaleTimeString()} 下次刷新:${nextRefreshTime.toLocaleTimeString()} `; autoBtn.textContent = this.isAutoRunning ? "停止运行" : "开始运行"; autoBtn.classList.toggle("active", this.isAutoRunning); }, async processPosts(retryCount = 0) { config.isProcessing = true; const posts = Array.from( document.querySelectorAll('div[action-type="feed_list_item"]') ).filter((post) => !config.processedPosts.has(post)); for (const post of posts) { if (weiboAPI.commentCount >= config.maxComment) { console.log("【限制触发】达到最大评论数"); continue; // 修改为 continue } const mid = post.getAttribute("mid"); if (!mid) continue; config.processedPosts.add(post); try { await this.processSinglePost(post, mid); // 强制等待 } catch (error) { console.error("处理帖子时发生错误:", error); config.requestQueue.delete(mid); // 异常时清理队列 } } config.isProcessing = false; }, // 新增等待方法 async waitForPosts(maxWait = 15000) { return new Promise((resolve) => { const startTime = Date.now(); const checkInterval = setInterval(() => { const found = document.querySelectorAll('div[action-type="feed_list_item"]') .length > 0; if (found || Date.now() - startTime > maxWait) { clearInterval(checkInterval); resolve(); } }, 500); }); }, // 新增空状态处理方法 async handleEmptyPosts(retryCount) { // 重置已处理记录防止污染 config.processedPosts.clear(); // 渐进式延迟:500ms * 重试次数(最高5秒) await new Promise((r) => setTimeout(r, Math.min(5000, 500 * (retryCount + 1))) ); // 优先尝试滚动加载(兼容瀑布流页面) window.scrollTo(0, document.body.scrollHeight); // 异步等待新内容加载 await new Promise((r) => setTimeout(r, 2000)); // 重新执行处理流程 return this.processPosts(retryCount + 1); }, async processSinglePost(post, mid) { try { if (config.requestQueue.has(mid)) return; config.requestQueue.add(mid); // 使用独立的try块包裹核心逻辑 try { // 强化异步操作监控 await Promise.allSettled([ (async () => { try { await this.tryLikePost(mid); } catch (e) { console.error("【点赞深层错误】", e); } })(), (async () => { try { await this.tryCommentPost(post, mid); } catch (e) { console.error("【评论深层错误】", e); } })() ]); } finally { // 新增内部finally保证清理 console.log("【内部清理】准备释放MID:", mid); } } catch (error) { console.error("【外层异常】", error); } finally { console.log("【队列跟踪】删除MID:", mid); config.requestQueue.delete(mid); console.log("【当前队列】", Array.from(config.requestQueue)); } }, async tryLikePost(mid) { try { return new Promise((resolve) => { weiboAPI.sendLikeRequest(mid, (success) => { if (success) { console.log("【点赞成功】MID:", mid); weiboAPI.likeCount++; // return true; } else { console.warn("【点赞失败】MID:", mid); } resolve(); }); }); } catch (e) { console.error("【点赞流程异常】", e); // return null; } }, async tryCommentPost(post, mid) { if (config.maxComment <= 0 || weiboAPI.commentCount >= config.maxComment) return; const content = weiboAPI.extractPostContent(post); if (!content) return; const prompt = `请直接回答问题,直接输出结果,根据以下微博内容生成一句积极温暖的回复:${content}`; const reply = await generateContent(prompt).catch(() => null); if (!reply) return; showNotice("回复的内容:" + reply); return new Promise((resolve) => { // ================ 修改后的回调处理 ================ weiboAPI.sendCommentRequest(mid, reply, (success) => { console.log("【调试】回调触发,success=", success); // 添加日志 if (success) { weiboAPI.commentCount++; console.log("【成功】评论计数增加,当前=", weiboAPI.commentCount); // return true; } else { console.warn("【失败】评论操作未成功"); // return false; } resolve(); }); }); }, checkLimits() { const isLimitReached = dailyCounter.count >= config.dailyLimit; if (isLimitReached) this.stopAuto("达到操作上限"); return isLimitReached; }, updateButton(text) { const btn = document.getElementById("weiboAutoBtn"); if (btn) { btn.disabled = text === "已达上限"; btn.textContent = text; } }, }; // API模块 const weiboAPI = { commentCount: 0, likeCount: 0, resetCounters() { this.commentCount = 0; this.likeCount = 0; }, getCsrfToken() { return document.cookie.match(/(^|; )XSRF-TOKEN=([^;]+)/)?.[2] || ""; }, buildHeaders() { return { "X-XSRF-TOKEN": this.getCsrfToken(), Cookie: document.cookie, Referer: location.href, "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", }; }, sendLikeRequest(mid, callback) { GM_xmlhttpRequest({ method: "POST", url: `https://${currentDomain}/aj/v6/like/add`, headers: this.buildHeaders(), data: new URLSearchParams({ ajwvr: 6, mid: mid, _t: Date.now(), }), onload: (res) => { console.log("【调试】评论接口响应:", res.responseText); // 打印完整响应 try { const response = safeParseJSON(res.responseText); console.log("【调试】解析后的响应:", response); // 打印解析后的响应 // 多维度验证成功条件 const isHttpSuccess = res.status >= 200 && res.status < 300; const isBusinessSuccess = response?.code === "100000"; if (isHttpSuccess && isBusinessSuccess) { console.log("【成功】点赞操作成功"); callback(true); // 明确传递成功状态 } else { console.warn("【失败】点赞操作未成功,响应:", response); callback(false); // 明确传递失败状态 } } catch (e) { console.error("【异常】响应解析失败:", e); callback(false); // 解析失败时传递失败状态 } }, onerror: () => callback(false), }); }, sendCommentRequest(mid, content, callback) { GM_xmlhttpRequest({ method: "POST", url: `https://${currentDomain}/aj/v6/comment/add`, headers: this.buildHeaders(), data: new URLSearchParams({ ajwvr: 6, mid: mid, content: content, _t: Date.now(), forward: config.forward, }), onload: (res) => { //console.log('【调试】评论接口响应:', res.responseText); // 打印完整响应 try { const response = safeParseJSON(res.responseText); // console.log('【调试】解析后的响应:', response); // 打印解析后的响应 // 多维度验证成功条件 const isHttpSuccess = res.status >= 200 && res.status < 300; const isBusinessSuccess = response?.code === "100000"; if (isHttpSuccess && isBusinessSuccess) { console.log("【成功】评论操作成功"); callback(true); // 明确传递成功状态 } else { console.warn("【失败】评论操作未成功,响应:", response); callback(false); // 明确传递失败状态 } } catch (e) { console.error("【异常】响应解析失败:", e); callback(false); // 解析失败时传递失败状态 } }, onerror: (err) => { console.error("【错误】网络请求失败:", err); callback(false); // 网络错误时传递失败状态 }, ontimeout: () => { console.error("【超时】请求超时"); callback(false); // 超时时传递失败状态 }, }); }, extractPostContent(post) { const contentDiv = post.querySelector(".WB_text.W_f14"); if (!contentDiv) return ""; const clone = contentDiv.cloneNode(true); //clone.querySelectorAll('a,script,style').forEach(n => n.remove()); clone .querySelectorAll('a[action-type="fl_unfold"]') .forEach((n) => n.remove()); return clone.textContent.replace(/\s+/g, " ").trim().slice(0, 300); // 限制内容长度 }, }; // 内容生成模块(保持原有逻辑) async function generateContent(content) { showNotice("请求的内容:" + content); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "https://api.siliconflow.cn/v1/chat/completions", headers: { Authorization: `Bearer ${config.deepseekToken}`, "Content-Type": "application/json", }, data: JSON.stringify({ model: "deepseek-ai/DeepSeek-V3", messages: [ { role: "user", content: content, }, ], stream: false, max_tokens: 150, stop: ["思考:", "分析:", "首先"], temperature: 0.3, top_p: 0.3, frequency_penalty: 0.5, presence_penalty: 0.3, n: 1, response_format: { type: "text" }, }), onload: function (response) { try { const result = JSON.parse(response.responseText); if (result.choices && result.choices[0].message.content) { resolve( result.choices[0].message.content .replace(/【\d+†.*】/g, "") // 清理特殊标记 .trim() ); } else { throw new Error("API返回格式异常"); } } catch (e) { console.error("解析失败:", e); reject(e); } }, onerror: function (err) { console.error("请求失败:", err); reject(err); }, }); }); } // ================ 安全JSON解析工具 ================ function safeParseJSON(str) { try { return JSON.parse(str); } catch (e) { console.warn("非JSON响应:", str); return null; } } // ================ 新增签到模块 ================ // 获取超话列表API async function fetchSuperTopics(page) { return new Promise((resolve, reject) => { // 直接从Cookie提取XSRF-TOKEN const xsrfMatch = document.cookie.match(/XSRF-TOKEN=([^;]+)/); console.log("当前XSRF-TOKEN:", xsrfMatch); // if (!xsrfMatch) { // reject(new Error('XSRF-TOKEN未找到')); // return; // } const apiUrl = `https://${currentDomain}/ajax/profile/topicContent?tabid=231093_-_chaohua&page=${page}`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, headers: { "X-XSRF-TOKEN": document.cookie.match(/(^|; )XSRF-TOKEN=([^;]+)/)?.[2] || "", // 关键修改点 Accept: "application/json", Cookie: document.cookie, "content-type": "application/json", Referer: location.href, }, responseType: "json", onload: (response) => { if (response.status === 200) { resolve(response.response); } else { reject(new Error(`API请求失败: ${response.status}`)); } }, onerror: (err) => { showNotice(err); reject(err); }, }); }); } // 递归获取所有超话 async function getAllSuperTopics() { let allTopics = []; let currentPage = 1; while (currentPage <= config.maxPages) { try { const response = await fetchSuperTopics(currentPage); if (!response?.data?.list?.length) break; allTopics = allTopics.concat(response.data.list); // 判断是否还有下一页 if (response.data.list.length < config.perPage) break; currentPage++; await new Promise((r) => setTimeout(r, 1000)); // 分页请求间隔 } catch (error) { console.error("获取超话列表失败:", error); break; } } return allTopics .map((topic) => { const match = topic.link?.match(/\/\/weibo\.com\/p\/(\w+)/); return match ? { id: match[1], name: topic.topic_name } : null; }) .filter(Boolean); } // 签到功能(保持原有逻辑) // 发送签到请求(含XSRF-TOKEN) function sendCheckinRequest(id, retryCount = 0) { return new Promise((resolve, reject) => { // 直接从Cookie提取XSRF-TOKEN const apiUrl = `https://${currentDomain}/p/aj/general/button?ajwvr=6&api=http://i.huati.weibo.com/aj/super/checkin&texta=%E5%B7%B2%E7%AD%BE%E5%88%B0&textb=%E5%B7%B2%E7%AD%BE%E5%88%B0&status=1&id=${id}`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, headers: { "X-XSRF-TOKEN": document.cookie.match(/(^|; )XSRF-TOKEN=([^;]+)/)?.[2] || "", // 关键修改点 Accept: "application/json", Cookie: document.cookie, Referer: location.href, }, responseType: "json", onload: function (response) { try { const res = response.response; if (res.code === "100000") { resolve({ id, success: true, msg: res.msg }); } else { resolve({ id, success: false, msg: res.msg || "未知错误" }); } } catch (e) { retryCount < config.retry ? reject(new Error(`解析失败,第${retryCount + 1}次重试`)) : resolve({ id, success: false, msg: "响应解析失败" }); } }, onerror: function () { retryCount < config.retry ? reject(new Error(`网络错误,第${retryCount + 1}次重试`)) : resolve({ id, success: false, msg: "网络请求失败" }); }, }); }); } // 签到控制器 async function startCheckin() { try { const topics = await getAllSuperTopics(); if (!topics.length) { showNotice("未获取到超话列表"); return; } let successCount = 0; for (const topic of topics) { try { const result = await sendCheckinRequest(topic.id); if (result.success) { successCount++; showNotice(`[${topic.name}] 签到成功`, 2000); } else { showNotice(`[${topic.name}]` + result.msg, 2000); } await new Promise((r) => setTimeout(r, config.delay)); } catch (error) { console.error(`[${topic.name}] 签到失败:`, error); } } showNotice(`签到完成 成功${successCount}/${topics.length}个`, 5000); } catch (error) { showNotice("签到出错:" + error.message); } } // 签到模块 const signManager = { get isSigned() { const lastSignDate = GM_getValue("lastSignDate", ""); const today = new Date().toDateString(); return lastSignDate === today; }, set isSigned(value) { const today = new Date().toDateString(); GM_setValue("lastSignDate", value ? today : ""); }, async startSign() { // if (this.isSigned) return; // 禁用按钮并显示签到中状态 // this.updateUI(true); try { await startCheckin(); showNotice("签到完成"); } catch (error) { console.error("签到失败:", error); showNotice("签到失败,请重试"); } }, }; // 发布微博模块 const postManager = { async trySentence() { const prompt2 = `模仿肖战粉丝语气生成一条微博(直接输出结果):要求口语化,别太浮夸,客观`; const reply = await generateContent(prompt2).catch(() => null); if (!reply) return; showNotice("生成的内容:" + reply); return new Promise((resolve) => { this.postWeibo(reply, () => { resolve(); }); }); }, async postWeibo(content, callback) { GM_xmlhttpRequest({ method: "POST", url: `https://${currentDomain}/p/aj/proxy`, headers: weiboAPI.buildHeaders(), data: new URLSearchParams({ ajwvr: 6, // mid: mid, _t: Date.now(), location: "page_100808_super_index", text: content, appkey: "", style_type: "1", pic_id: "", tid: "", pdetail: "100808f57b3faeb25f86b43a8cc58f02286f40", mid: "", isReEdit: "false", sync_wb: "1", pub_source: "page_1", api: "http://i.huati.weibo.com/pcpage/operation/publisher/sendcontent?sign=super&page_id=100808f57b3faeb25f86b43a8cc58f02286f40", object_id: "1022:100808f57b3faeb25f86b43a8cc58f02286f40", module: "publish_913", page_module_id: "913", longtext: "1", topic_id: "1022:100808f57b3faeb25f86b43a8cc58f02286f40", pub_type: "dialog", _t: "0", }), onload: (res) => { const success = res.status === 200 && JSON.parse(res.responseText).code === "100000"; callback(success); }, onerror: () => callback(false), }); }, sendPostRequest(url, params) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: url, headers: { "X-XSRF-TOKEN": document.cookie.match(/(^|; )XSRF-TOKEN=([^;]+)/)?.[2] || "", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", Cookie: document.cookie, Referer: location.href, }, data: params.toString(), onload: function (response) { try { const res = JSON.parse(response.responseText); resolve(res); } catch (e) { reject(new Error("解析响应失败")); } }, onerror: function (error) { reject(new Error("网络请求失败")); }, }); }); }, }; // 界面模块 function createControlPanel() { const panel = document.createElement("div"); panel.id = "weibotoolPanel"; // 签到按钮 const signBtn = document.createElement("button"); signBtn.className = "weibotool-btn"; signBtn.id = "signBtn"; signBtn.textContent = "一键签到"; signBtn.onclick = () => signManager.startSign(); // 自动互动按钮 const autoBtn = document.createElement("button"); autoBtn.className = "weibotool-btn"; autoBtn.id = "autoBtn"; autoBtn.textContent = "启动自动互动(进超话主页可用)"; autoBtn.onclick = () => controller.toggleAuto(); // 发布微博按钮 const postBtn = document.createElement("button"); postBtn.className = "weibotool-btn"; postBtn.id = "postBtn"; postBtn.textContent = "发布微博"; postBtn.onclick = () => postManager.trySentence(); // 状态信息 const statusInfo = document.createElement("div"); statusInfo.id = "statusInfo"; panel.append(signBtn, autoBtn, postBtn, statusInfo); document.body.appendChild(panel); // 初始化UI状态 // dailyCounter.reset(); } // 初始化时恢复状态 window.addEventListener("load", () => { createControlPanel(); showNotice("插件已加载"); setTimeout(function () { const items = document.querySelectorAll( 'div[action-type="feed_list_item"]' ); if (items) { console.log("找到的元素数量:", items.length); // 初始化控制器 controller.init(); } else { console.log("未找到元素"); } }, 1000); // 延迟 1 秒 }); // 修改后的showNotice函数 function showNotice(text, duration = 3000) { const div = document.createElement("div"); div.className = "weibotool-notice"; div.textContent = text; document.body.appendChild(div); setTimeout(() => div.remove(), duration); } })();