微博自动互动插件
// ==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
}<br>
${
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);
}
})();