// ==UserScript== // @name 论坛帖子批量选择小助手 // @namespace http://tampermonkey.net/ // @version 1.6 // @description 在任何网页中多选帖子并批量打开,支持屏蔽词功能 // @author 黄萌萌可爱多 // @match *://*/* // @license MIT // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/525999/%E8%AE%BA%E5%9D%9B%E5%B8%96%E5%AD%90%E6%89%B9%E9%87%8F%E9%80%89%E6%8B%A9%E5%B0%8F%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/525999/%E8%AE%BA%E5%9D%9B%E5%B8%96%E5%AD%90%E6%89%B9%E9%87%8F%E9%80%89%E6%8B%A9%E5%B0%8F%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; // 存储选中的帖子链接 let selectedPosts = []; // 存储第一次选中的帖子索引 let firstSelectedIndex = -1; // 存储最后一次选中的帖子索引 let lastSelectedIndex = -1; // 获取屏蔽词列表 let blacklist = GM_getValue('blacklist', '').split(' ').filter(Boolean); // 获取所有可点击的链接元素 const clickableLinks = Array.from(document.querySelectorAll('a[href]')); // 创建已选择链接数量显示元素 const selectedCountDisplay = document.createElement('div'); selectedCountDisplay.style.position = 'fixed'; selectedCountDisplay.style.top = '10px'; selectedCountDisplay.style.right = '200px'; // 调整位置以避免与按钮重叠 selectedCountDisplay.style.zIndex = '9999'; selectedCountDisplay.style.padding = '10px 20px'; selectedCountDisplay.style.backgroundColor = '#333'; selectedCountDisplay.style.color = 'white'; selectedCountDisplay.style.borderRadius = '5px'; selectedCountDisplay.textContent = '已选择的链接:0'; // 初始化显示 // 更新选中链接数量显示的函数 function updateSelectedCountDisplay() { selectedCountDisplay.textContent = `已选择的链接:${selectedPosts.length}`; } // 添加点击事件监听 clickableLinks.forEach((link, index) => { link.addEventListener('click', function(event) { const postLink = this.href; // 获取链接 const linkText = this.textContent.trim(); // 检查是否为时间格式(如 YYYY-MM-DD HH:MM) const isTimeFormat = /^\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{2}$/.test(linkText); // 检查是否为纯数字、纯英文或数字加英文(可以包含符号,但不包含中文字符) const isPureNumericOrAlphanumeric = /^[a-zA-Z0-9\s\p{P}]*$/.test(linkText) && !/[\u4e00-\u9fa5]/u.test(linkText); // 检查文本长度是否少于个10字符 const isShortText = linkText.length < 10; // 修改为不少于10字符 // 修改为确保链接文本不包含任何一个屏蔽词 const containsBlacklistWord = blacklist.some(word => linkText.includes(word)); // 如果满足以下条件之一,则不选择该链接并应用屏蔽样式 if ((isTimeFormat || isPureNumericOrAlphanumeric || isShortText || containsBlacklistWord) && (event.ctrlKey || event.shiftKey)) { this.classList.add('blacklisted'); // 添加屏蔽样式 setTimeout(() => this.classList.remove('blacklisted'), 200); // 闪2次 setTimeout(() => this.classList.add('blacklisted'), 400); setTimeout(() => this.classList.remove('blacklisted'), 600); // 如果链接符合屏蔽条件,阻止其在按住 Ctrl 或 Shift 键时被选中 if (event.ctrlKey || event.shiftKey) { event.preventDefault(); // 阻止默认的链接跳转行为 return; // 不进行任何操作,允许默认行为 } } // 如果没有按下 Ctrl 或 Shift 键,并且没有选中的链接 if (!event.ctrlKey && !event.shiftKey) { // 如果已经有选中的链接,并且再次点击任意一条被选择的链接 if (selectedPosts.includes(postLink)) { event.preventDefault(); // 阻止默认的链接跳转行为 selectedPosts.forEach(link => { window.open(link, '_blank', 'noopener,noreferrer'); // 确保打开新标签页但不切换焦点且静默打开 }); selectedPosts = []; // 清空选中列表 clickableLinks.forEach(link => link.classList.remove('selected')); firstSelectedIndex = -1; // 重置第一次选中的链接索引 lastSelectedIndex = -1; // 重置最后一次选中的链接索引 } else { // 允许用户点击并打开任何链接 return; // 不进行任何操作,允许默认行为 } } // 检查是否按下了 Ctrl 键 if (event.ctrlKey) { event.preventDefault(); // 阻止默认的链接跳转行为 if (!selectedPosts.includes(postLink)) { selectedPosts.push(postLink); this.classList.add('selected'); // 添加选中样式 } else { selectedPosts = selectedPosts.filter(link => link !== postLink); this.classList.remove('selected'); } } // 检查是否按下了 Shift 键 else if (event.shiftKey) { event.preventDefault(); // 阻止默认的链接跳转行为 if (firstSelectedIndex === -1) { // 如果是第一次按下 Shift 键,记录第一个选中的链接索引 firstSelectedIndex = index; } // 更新最后一个选中的链接索引 lastSelectedIndex = index; // 确定选择范围 const startIndex = Math.min(firstSelectedIndex, lastSelectedIndex); const endIndex = Math.max(firstSelectedIndex, lastSelectedIndex); // 清空之前的选中状态 selectedPosts = []; clickableLinks.forEach(l => l.classList.remove('selected')); // 选择范围内的所有链接 for (let i = startIndex; i <= endIndex; i++) { const linkText = clickableLinks[i].textContent.trim(); const isTimeFormat = /^\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{2}$/.test(linkText); const isPureNumericOrAlphanumeric = /^[a-zA-Z0-9\s\p{P}]*$/.test(linkText) && !/[\u4e00-\u9fa5]/u.test(linkText); const isShortText = linkText.length < 10; const containsBlacklistWord = blacklist.some(word => linkText.includes(word)); if (!isTimeFormat && !isPureNumericOrAlphanumeric && !isShortText && !containsBlacklistWord) { selectedPosts.push(clickableLinks[i].href); clickableLinks[i].classList.add('selected'); } } } // 更新选中链接数量显示 updateSelectedCountDisplay(); }); }); // 监听点击页面非链接位置的事件 document.addEventListener('click', function(event) { // 检查点击的是否是非链接位置 if (!event.target.closest('a[href]')) { // 清空选中列表 selectedPosts = []; clickableLinks.forEach(link => link.classList.remove('selected')); firstSelectedIndex = -1; // 重置第一次选中的链接索引 lastSelectedIndex = -1; // 重置最后一次选中的链接索引 // 更新选中链接数量显示 updateSelectedCountDisplay(); } }); // 创建批量打开按钮 const openButton = document.createElement('button'); openButton.textContent = '批量打开选中的链接'; openButton.style.position = 'fixed'; openButton.style.top = '10px'; openButton.style.right = '10px'; openButton.style.zIndex = '9999'; openButton.style.padding = '10px 20px'; openButton.style.backgroundColor = '#4CAF50'; openButton.style.color = 'white'; openButton.style.border = 'none'; openButton.style.borderRadius = '5px'; openButton.style.cursor = 'pointer'; openButton.style.boxShadow = '2px 2px 5px rgba(0, 0, 0, 0.3)'; // 添加按钮点击事件 openButton.addEventListener('click', function() { selectedPosts.forEach(link => { window.open(link, '_blank', 'noopener,noreferrer'); // 确保打开新标签页但不切换焦点且静默打开 }); selectedPosts = []; // 清空选中列表 clickableLinks.forEach(link => link.classList.remove('selected')); firstSelectedIndex = -1; // 重置第一次选中的链接索引 lastSelectedIndex = -1; // 重置最后一次选中的链接索引 window.focus(); // 确保当前窗口保持焦点 // 更新选中链接数量显示 updateSelectedCountDisplay(); }); // 创建清除选择按钮 const clearButton = document.createElement('button'); clearButton.textContent = '清除选择'; clearButton.style.position = 'fixed'; clearButton.style.top = '50px'; clearButton.style.right = '10px'; clearButton.style.zIndex = '9999'; clearButton.style.padding = '10px 20px'; clearButton.style.backgroundColor = '#f44336'; clearButton.style.color = 'white'; clearButton.style.border = 'none'; clearButton.style.borderRadius = '5px'; clearButton.style.cursor = 'pointer'; clearButton.style.boxShadow = '2px 2px 5px rgba(0, 0, 0, 0.3)'; // 添加清除选择按钮点击事件 clearButton.addEventListener('click', function() { selectedPosts = []; // 清空选中列表 clickableLinks.forEach(link => link.classList.remove('selected')); firstSelectedIndex = -1; // 重置第一次选中的链接索引 lastSelectedIndex = -1; // 重置最后一次选中的链接索引 // 更新选中链接数量显示 updateSelectedCountDisplay(); }); // 将按钮添加到页面 document.body.appendChild(openButton); document.body.appendChild(clearButton); // 将已选择链接数量显示元素添加到页面 document.body.appendChild(selectedCountDisplay); // 添加选中样式 const style = document.createElement('style'); style.textContent = ` a.selected { background-color: #87CEEB; /* 修改选中颜色为天蓝色 */ } a.blacklisted { background-color: #FF0000; /* 修改屏蔽颜色为红色 */ animation: blink 1s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } `; document.head.appendChild(style); // 添加油猴设置菜单项 GM_registerMenuCommand('⚙️ 设置', () => { // 创建设置弹窗 const settingsBox = document.createElement('div'); settingsBox.innerHTML = `
作者: 黄萌萌可爱多
版本: 1.5.1