// ==UserScript== // @name 微博自动互动插件 // @namespace http://tampermonkey.net/ // @version 1.8 // @description 安全可控的微博自动互动插件 // @author Your Name // @match https://*.weibo.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @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 currentDomain = window.location.hostname; const config = { deepseekToken: "sk-oqwogagrgsaimscnmjitdnhcruyftjsaqsbjsvppsfrberps", interval: 15 * 60 * 1000, maxComment: 5, maxLike: 5, dailyLimit: 50, 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://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); } }); }); } // ================ 新增签到模块 ================ // 获取超话列表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); } })();