// ==UserScript==
// @name QQ空间自动点赞 (v6.1 界面修复版)
// @namespace https://scriptcat.org/
// @version 6.1
// @description 自动点赞 + 自动滚动加载。修复GUI不显示的问题,支持好友主页穿透,内置黑白名单。
// @author AI Assistant
// @match https://user.qzone.qq.com/*
// @match https://qzone.qq.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// --- 默认配置 ---
const DEFAULT_CONFIG = {
mode: 'all', // all | whitelist | blacklist
names: '', // 名单
minDelay: 800, // 最小点赞延迟
maxDelay: 1500, // 最大点赞延迟
enable: true, // 运行开关
autoScroll: true, // 是否自动滚动
scrollStep: 600 // 滚动步长
};
// --- 样式定义 ---
const STYLES = `
#qz-helper-btn {
position: fixed;
bottom: 80px; /* 稍微调高一点,防止被底部栏遮挡 */
right: 20px;
z-index: 2147483647; /* 最大层级,保证不被遮挡 */
background: linear-gradient(135deg, #2196F3, #21CBF3);
color: #fff;
border: none;
border-radius: 50px;
padding: 12px 24px;
font-size: 14px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
cursor: pointer;
transition: all 0.3s;
font-family: "Microsoft YaHei", sans-serif;
}
#qz-helper-btn:hover { transform: scale(1.1); box-shadow: 0 6px 20px rgba(33, 203, 243, 0.6); }
#qz-settings-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 420px;
background: #fff;
z-index: 2147483647;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
padding: 20px;
font-family: "Microsoft YaHei", sans-serif;
display: none;
border: 1px solid #ccc;
}
.qz-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 15px; }
.qz-header h3 { margin: 0; font-size: 18px; color: #333; }
.qz-close { cursor: pointer; font-size: 24px; color: #999; padding: 0 5px; }
.qz-form-item { margin-bottom: 15px; }
.qz-form-item label { display: block; margin-bottom: 5px; font-weight: bold; font-size: 13px; color: #555; }
.qz-tip { font-size: 12px; color: #888; font-weight: normal; margin-left: 5px; }
.qz-input-box {
width: 100%; box-sizing: border-box; padding: 10px;
border: 1px solid #ddd; border-radius: 6px; font-size: 13px; outline: none;
background: #f9f9f9;
}
.qz-input-box:focus { border-color: #2196F3; background: #fff; }
textarea.qz-input-box { height: 80px; resize: vertical; }
.qz-flex-row { display: flex; gap: 10px; align-items: center; }
.qz-footer { margin-top: 20px; text-align: right; }
.qz-btn { padding: 10px 30px; border-radius: 6px; border: none; cursor: pointer; font-size: 14px; font-weight: bold; transition: background 0.2s;}
.qz-btn-primary { background: #2196F3; color: white; }
.qz-btn-primary:hover { background: #1976D2; }
.qz-status-bar { margin-top: 15px; font-size: 12px; color: #666; background: #f0f7ff; padding: 10px; border-radius: 4px; display: flex; justify-content: space-between;}
`;
GM_addStyle(STYLES);
// --- 变量 ---
let config = GM_getValue('qz_config_v6', DEFAULT_CONFIG);
let isRunning = false;
// --- GUI 构建 ---
function createGUI() {
// 防止重复创建
if (document.getElementById('qz-helper-btn')) return;
const btn = document.createElement('button');
btn.id = 'qz-helper-btn';
btn.innerHTML = '📜 点赞设置';
btn.onclick = togglePanel;
document.body.appendChild(btn);
const panel = document.createElement('div');
panel.id = 'qz-settings-panel';
panel.innerHTML = `
状态: 就绪
`;
document.body.appendChild(panel);
panel.querySelector('.qz-close').onclick = togglePanel;
panel.querySelector('#qz-save').onclick = saveConfig;
}
function togglePanel() {
const panel = document.getElementById('qz-settings-panel');
if (!panel) {
createGUI(); // 如果面板意外消失,重新创建
return;
}
if (panel.style.display === 'block') {
panel.style.display = 'none';
} else {
loadConfigToUI();
panel.style.display = 'block';
}
}
// 注册脚本猫/Tampermonkey 菜单命令 (备用入口)
GM_registerMenuCommand("⚙️ 打开点赞设置", togglePanel);
function loadConfigToUI() {
config = GM_getValue('qz_config_v6', DEFAULT_CONFIG);
document.getElementById('qz-enable').value = config.enable.toString();
document.getElementById('qz-auto-scroll').value = (config.autoScroll !== false).toString();
document.getElementById('qz-mode').value = config.mode;
document.getElementById('qz-names').value = config.names;
document.getElementById('qz-min-delay').value = config.minDelay;
document.getElementById('qz-max-delay').value = config.maxDelay;
}
function saveConfig() {
const newConfig = {
enable: document.getElementById('qz-enable').value === 'true',
autoScroll: document.getElementById('qz-auto-scroll').value === 'true',
scrollStep: 600,
mode: document.getElementById('qz-mode').value,
names: document.getElementById('qz-names').value,
minDelay: parseInt(document.getElementById('qz-min-delay').value) || 800,
maxDelay: parseInt(document.getElementById('qz-max-delay').value) || 1500
};
GM_setValue('qz_config_v6', newConfig);
config = newConfig;
const btn = document.getElementById('qz-save');
btn.innerText = "已保存!";
setTimeout(() => {
btn.innerText = "保存设置";
document.getElementById('qz-settings-panel').style.display = 'none';
}, 800);
updateStatus("配置更新,脚本运行中...");
}
function updateStatus(text) {
const el = document.getElementById('qz-status-text');
if (el) el.innerText = text;
}
// --- 核心逻辑 ---
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
const randomSleep = (min, max) => sleep(Math.floor(Math.random() * (max - min + 1) + min));
const getNameList = () => {
if (!config.names) return [];
return config.names.split(/[,,\n]/).map(s => s.trim()).filter(s => s);
};
const checkPermission = (author) => {
const list = getNameList();
if (config.mode === 'all') return true;
if (config.mode === 'whitelist') return list.includes(author);
if (config.mode === 'blacklist') return !list.includes(author);
return false;
};
const getAllFeeds = () => {
let feeds = [];
try {
const mainFeeds = document.querySelectorAll('.f-single');
if(mainFeeds.length > 0) feeds = feeds.concat(Array.from(mainFeeds));
} catch(e) {}
try {
const iframe = document.getElementById('app_canvas_frame');
if (iframe && iframe.contentDocument) {
const frameFeeds = iframe.contentDocument.querySelectorAll('.f-single');
if(frameFeeds.length > 0) feeds = feeds.concat(Array.from(frameFeeds));
}
} catch (e) {}
return feeds;
};
const performScroll = () => {
const iframe = document.getElementById('app_canvas_frame');
if (iframe && iframe.contentDocument) {
iframe.contentDocument.documentElement.scrollTop += config.scrollStep;
iframe.contentDocument.body.scrollTop += config.scrollStep;
}
window.scrollBy({ top: config.scrollStep, behavior: 'smooth' });
};
const mainTask = async () => {
if (isRunning) return;
config = GM_getValue('qz_config_v6', DEFAULT_CONFIG);
if (!config.enable) return;
isRunning = true;
updateStatus("正在扫描...");
try {
// 自动点击新动态按钮
const newFeedBtn = document.querySelector('a.feed-update');
if (newFeedBtn && newFeedBtn.offsetParent !== null) {
newFeedBtn.click();
await sleep(2000);
}
const feeds = getAllFeeds();
let actionCount = 0;
for (const feed of feeds) {
if (!config.enable) break;
const likeBtn = feed.querySelector('.qz_like_btn_v3');
if (!likeBtn) continue;
if (likeBtn.classList.contains('item-on')) continue;
let author = "";
let nickEl = feed.querySelector('.f-nick') || feed.querySelector('.user-info .nickname');
if (nickEl) author = nickEl.innerText.trim();
else {
const ownerEl = document.querySelector('.head-detail .user-name') ||
(document.getElementById('app_canvas_frame') &&
document.getElementById('app_canvas_frame').contentDocument.querySelector('.user-name'));
if(ownerEl) author = ownerEl.innerText.trim();
}
if (!author) continue;
if (checkPermission(author)) {
updateStatus(`点赞: ${author}`);
likeBtn.click();
actionCount++;
await randomSleep(config.minDelay, config.maxDelay);
}
}
// 滚动逻辑
if (config.enable && config.autoScroll) {
if (actionCount === 0) {
updateStatus("当前屏完毕,向下滚动...");
performScroll();
} else {
updateStatus(`已赞 ${actionCount} 条,继续...`);
await sleep(800);
performScroll();
}
} else {
if (actionCount === 0) updateStatus("无新赞,等待中...");
}
} catch (e) {
console.error("Auto Like Error:", e);
} finally {
isRunning = false;
}
};
// --- 启动逻辑修复 ---
function init() {
createGUI();
// 启动主任务循环
setTimeout(mainTask, 2000);
setInterval(mainTask, 4000);
console.log("QQ空间助手 v6.1 已启动");
}
// 确保页面加载后执行
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
window.addEventListener('load', init);
}
})();