// ==UserScript== // @name 微博自动互动插件 // @namespace http://tampermonkey.net/ // @version 1.9 // @description 安全可控的微博自动互动插件 // @author Your Name // @match https://*.weibo.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @connect ark.cn-beijing.volces.com // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js // ==/UserScript== (function () { "use strict"; // 动态获取当前域名 const currentDomain = window.location.hostname; // 将密钥转换为字节数组 function keyToBytes(key) { const bytes = []; for (let i = 0; i < key.length; i += 2) { bytes.push(parseInt(key.substr(i, 2), 16)); } return bytes; } // XOR 加密/解密函数 function xorCrypto(input, keyBytes) { let output = ""; for (let i = 0; i < input.length; i++) { const charCode = input.charCodeAt(i) ^ keyBytes[i % keyBytes.length]; output += String.fromCharCode(charCode); } return output; } // 加密函数 function encrypt(plaintext, SECRET_KEY) { const keyBytes = keyToBytes(SECRET_KEY); return btoa(xorCrypto(plaintext, keyBytes)); // 使用 Base64 编码 } // 解密函数 function decryptToken(ciphertext, SECRET_KEY) { const keyBytes = keyToBytes(SECRET_KEY); const decoded = atob(ciphertext); // 使用 Base64 解码 return xorCrypto(decoded, keyBytes); } // 使用示例 --------------------------------------------------- // 生成密钥(推荐使用安全随机数生成) const secretKey = "666f2fdde94d9f0d1120404a85b1d800494556a7815bef3c526389ea523c45f4"; // CryptoJS.lib.WordArray.random(256/8).toString(); const encryptedToken = "A1wd698rqTw8QyUvt5zsYnEge57iOd0RYga60mBfJ5JUWRbq"; //"e3266f61-cee2-4b8e-9cb2-0e382cbf2697" // 解密验证 // 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, interval: 15 * 60 * 1000, maxComment: 5, maxLike: 5, dailyLimit: 100, processedPosts: new Set(), requestQueue: new Set(), isProcessing: false, maxPages: 5, perPage: 20, retry: 3, delay: 1000, superTopicUrl: `https://${currentDomain}/p/100808f57b3faeb25f86b43a8cc58f02286f40/super_index`, feedRefreshInterval: 10 * 60 * 1000, // 新增10分钟定时器 }; // 新增Feed数据获取模块 const feedFetcher = { currentTimer: null, lastHtml: "", async fetchFeedHTML() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: config.superTopicUrl, headers: { "X-XSRF-TOKEN": document.cookie.match(/(^|; )XSRF-TOKEN=([^;]+)/)?.[2] || "", Cookie: document.cookie, Referer: location.href, }, onload: (res) => res.status === 200 ? resolve(res.responseText) : reject(), onerror: () => reject(), }); }); }, parseFeedHTML(html) { const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); // 解析后的HTML文档 let result = []; // 🔴 关键修改:使用doc而非document doc.querySelectorAll("script").forEach((script) => { // 使用doc对象 const scriptContent = script.textContent.trim(); if (scriptContent.startsWith("FM.view(")) { try { const jsonStr = scriptContent.slice( scriptContent.indexOf("{"), scriptContent.lastIndexOf("}") + 1 ); const data = JSON.parse(jsonStr); // 在调用 replace 前添加空值检查 if (data && data.html) { const htmlContent = data.html.replace(/\\/g, ""); const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlContent; result.push( ...Array.from( tempDiv.querySelectorAll('div[action-type="feed_list_item"]') ) ); // 继续处理 } else { // console.error('未找到 HTML 内容:', data); return; // 跳出当前 script 的处理 } } catch (e) { //console.error('解析失败:', e); return; // 跳出当前 script 的处理 } } }); return result; }, async getFreshPosts() { try { const html = await this.fetchFeedHTML(); if (html === this.lastHtml) return []; this.lastHtml = html; return this.parseFeedHTML(html); } catch (e) { console.error("获取feed失败:", e); return []; } }, startAutoRefresh(callback) { this.stopAutoRefresh(); this.currentTimer = setInterval(async () => { const posts = await this.getFreshPosts(); console.log("解析后的posts:", posts); callback(posts); }, config.feedRefreshInterval); }, stopAutoRefresh() { if (this.currentTimer) clearInterval(this.currentTimer); }, }; const dailyCounter = { get count() { const lastDate = GM_getValue("lastDate", ""); const today = new Date().toDateString(); if (lastDate !== today) return 0; return GM_getValue("dailyCount", 0); }, set count(value) { const today = new Date().toDateString(); GM_setValue("lastDate", today); GM_setValue("dailyCount", value); }, resetIfNewDay() { const lastDate = GM_getValue("lastDate", ""); const today = new Date().toDateString(); if (lastDate !== today) this.count = 0; }, }; // 样式调整 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; } `); // 在 controller 对象中修改 executeAutoTasks 方法 const controller = { refreshTimer: null, isAutoRunning: GM_getValue("isAutoRunning", false), toggleAuto() { this.isAutoRunning ? this.stopAuto() : this.startAuto(); }, startAuto() { // 如果是恢复任务,跳过状态检查 if (dailyCounter.count >= config.dailyLimit) { return; } this.isAutoRunning = true; GM_setValue("isAutoRunning", true); this.updateUI(); showNotice("自动互动已启动"); // 清除旧定时器 clearTimeout(this.refreshTimer); this.executeAutoTasks(); }, stopAuto(reason = "手动停止") { feedFetcher.stopAutoRefresh(); clearTimeout(this.refreshTimer); this.isAutoRunning = false; GM_setValue("isAutoRunning", false); this.updateUI(); showNotice(`自动互动已停止:${reason}`); }, async executeAutoTasks() { if (dailyCounter.count >= config.dailyLimit) { this.stopAuto("已达每日上限"); return; } try { const freshPosts = await feedFetcher.getFreshPosts(); await this.processPosts(freshPosts); // 修改为处理新获取的帖子 weiboAPI.commentCount = 0; weiboAPI.likeCount = 0; dailyCounter.count += 1; } catch (error) { console.error("自动互动失败:", error); } finally { if (this.isAutoRunning) { feedFetcher.startAutoRefresh(this.processPosts.bind(this)); // 启动定时刷新 } } }, async processPosts(posts = []) { if (config.isProcessing) return; config.isProcessing = true; try { for (const post of posts) { if (weiboAPI.commentCount >= config.maxComment) break; const mid = post.getAttribute("mid"); if (!mid || config.processedPosts.has(mid)) continue; config.processedPosts.add(mid); // 改为存储mid await this.processSinglePost(post, mid); } } finally { config.isProcessing = false; } }, updateUI() { const autoBtn = document.getElementById("autoBtn"); const status = document.getElementById("statusInfo"); if (!autoBtn || !status) return; const isLimitReached = dailyCounter.count >= config.dailyLimit; autoBtn.disabled = isLimitReached; autoBtn.textContent = this.isAutoRunning ? "停止互动" : "启动互动"; autoBtn.style.background = this.isAutoRunning ? "#ff4444" : "#ff8200"; status.innerHTML = `今日互动:${dailyCounter.count}/${ config.dailyLimit }
${ this.isAutoRunning ? `下次刷新:${new Date( Date.now() + config.interval ).toLocaleTimeString()}` : "自动互动已停止" }`; }, async processSinglePost(post, mid) { try { if (config.requestQueue.has(mid)) return; config.requestQueue.add(mid); // 并行处理点赞和评论 await Promise.allSettled([ this.tryLikePost(mid), this.tryCommentPost(post, mid), ]); } finally { config.requestQueue.delete(mid); } }, async tryLikePost(mid) { if ( weiboAPI.likeCount >= config.maxLike || dailyCounter.count >= config.dailyLimit ) return; return new Promise((resolve) => { weiboAPI.sendLikeRequest(mid, (success) => { if (success) weiboAPI.likeCount++; resolve(); }); }); }, 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) => { if (success) weiboAPI.commentCount++; 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, 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) => { const success = res.status === 200 && JSON.parse(res.responseText).code === "100000"; callback(success); }, 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: 1, // act=post&mid=5135404132472921&uid=7826221476&forward=1&isroot=0&content=%5B%E5%A4%AA%E5%BC%80%E5%BF%83%5D&location=page_100808_super_index&module=scommlist&group_source=&filter_actionlog=&pdetail=100808f57b3faeb25f86b43a8cc58f02286f40&_t=0 }), onload: (res) => { const success = res.status === 200 && JSON.parse(res.responseText).code === "100000"; console.log("评论结果:", success); callback(success); }, onerror: () => 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://ark.cn-beijing.volces.com/api/v3/chat/completions", headers: { Authorization: `Bearer ${config.deepseekToken}`, "Content-Type": "application/json", }, data: JSON.stringify({ model: "deepseek-v3-241226", 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); }, }); }); } // ================ 新增签到模块 ================ // 获取超话列表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.resetIfNewDay(); } // 初始化时启动自动刷新 window.addEventListener("load", () => { createControlPanel(); showNotice("插件已加载"); if (controller.isAutoRunning) { setTimeout(() => { controller.startAuto(); feedFetcher.startAutoRefresh(controller.processPosts.bind(controller)); }, 3000); } }); // 修改后的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); } })();