如题,由于论坛的内置搜索实在无能,所以我使用DeepSeek,Grok和Claude制作了一个油猴脚本,利用bing搜索引擎限定搜索范围进行站内搜索(与吾爱破解论坛搜索实现方式相似),源码如下
// ==UserScript==
// @name 智教联盟论坛搜索修复
// @namespace https://github.com/
// @version 1.7
// @description 修复forum.smart-teach.cn论坛搜索功能,使用必应搜索引擎搜索论坛内容
// @author 无不滑稽(以及DeepSeek,Grok和Claude组成的主力)
// @match *://forum.smart-teach.cn/*
// @icon https://forum.smart-teach.cn/favicon.ico
// @grant GM_openInTab
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_notification
// @license 随你咯,反正AI写的(?)
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// 配置
const CONFIG = {
searchEngine: 'bing',
searchEngines: {
bing: 'https://www.bing.com/search?q={query}',
baidu: 'https://www.baidu.com/s?wd={query}',
google: 'https://www.google.com/search?q={query}'
},
searchSelectors: [
'header input[type="search"]',
'.App-header input[type="search"]',
'.Search-input',
'input[aria-label="搜索"]',
'input[placeholder*="搜索论坛"]',
'input[placeholder*="Search forum"]'
],
debug: false,
historySize: 10,
autoAddSiteRestriction: true,
siteRestriction: 'site:forum.smart-teach.cn'
};
let searchHistory = [];
function init() {
log('智教联盟论坛搜索修复脚本已加载');
loadSearchHistory();
// 清理之前版本误判的补丁
cleanupInvalidPatches();
observeDOMChanges();
setTimeout(findAndPatchSearchBox, 1500);
setTimeout(findAndPatchSearchBox, 5000);
addSettingsMenu();
}
// 新增:清理被误判打上补丁的输入框
function cleanupInvalidPatches() {
const patchedInputs = document.querySelectorAll('input[data-patched="true"]');
let cleanedCount = 0;
patchedInputs.forEach(input => {
// 重新用严格规则验证
const type = (input.type || '').toLowerCase();
const ariaLabel = (input.getAttribute('aria-label') || '').toLowerCase();
const placeholder = (input.placeholder || '').toLowerCase();
// 如果不是真正的搜索框(type!=search 或 aria-label!=搜索),清理它
if (type !== 'search' || ariaLabel !== '搜索') {
log(`清理误判的补丁: placeholder="${input.placeholder}"`);
// 恢复原始 placeholder
if (input.dataset.originalPlaceholder) {
input.placeholder = input.dataset.originalPlaceholder;
}
// 移除补丁标记
delete input.dataset.patched;
delete input.dataset.originalPlaceholder;
// 移除事件监听器
input.removeEventListener('keydown', handleSearchKeyDown);
cleanedCount++;
}
});
if (cleanedCount > 0) {
log(`已清理 ${cleanedCount} 个误判的补丁`);
}
}
function findAndPatchSearchBox() {
log('正在查找搜索框...');
let searchInput = null;
for (const selector of CONFIG.searchSelectors) {
try {
const elements = document.querySelectorAll(selector);
for (const element of elements) {
if (isValidSearchBox(element)) {
searchInput = element;
log(`找到搜索框: ${selector}`);
break;
}
}
if (searchInput) break;
} catch (e) {
log(`选择器 ${selector} 错误: ${e.message}`);
}
}
if (!searchInput) {
searchInput = findSearchBoxByTraversal();
}
if (searchInput) {
patchSearchBox(searchInput);
return true;
}
log('未找到搜索框,将在DOM变化时重试');
return false;
}
function isValidSearchBox(element) {
if (!element || element.offsetParent === null) return false;
const placeholder = (element.placeholder || '').toLowerCase();
const type = (element.type || '').toLowerCase();
const ariaLabel = (element.getAttribute('aria-label') || '').toLowerCase();
// 严格排除关键词
const excludeKeywords = [
'标题', 'title', 'subject', '帖子标题', 'topic title',
'回复', 'reply', 'content', '正文', 'body',
'编辑', 'edit', '用户名', 'username', 'email',
'标签', 'tag', '主标签', '副标签', '请选择'
];
if (excludeKeywords.some(kw => placeholder.includes(kw) || ariaLabel.includes(kw))) {
log(`排除 - 含禁词: ${placeholder || ariaLabel}`);
return false;
}
// 检查上下文
let parent = element.parentElement;
let contextText = '';
for (let i = 0; i < 5 && parent; i++) {
contextText += (parent.textContent || '').toLowerCase() + ' ';
parent = parent.parentElement;
}
if (/发布.*主题|回复.*主题|编辑.*帖子|create.*discussion|reply|post|添加标签|选择标签/i.test(contextText)) {
log('排除 - 上下文含发帖/回复/标签相关文字');
return false;
}
// 可见性检查
const style = window.getComputedStyle(element);
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
return false;
}
// **核心判断:必须同时满足 type=search 且 aria-label="搜索"**
const isSearchBox = type === 'search' && ariaLabel === '搜索';
if (!isSearchBox) {
log(`非搜索框 - type: ${type}, aria-label: ${ariaLabel}`);
}
return isSearchBox;
}
function findSearchBoxByTraversal() {
log('通过DOM遍历查找搜索框...');
const header = document.querySelector('header, .App-header, #header-primary');
if (header) {
const inputs = header.querySelectorAll('input[type="search"][aria-label="搜索"]');
for (const input of inputs) {
if (isValidSearchBox(input)) {
log('在header区域找到搜索框');
return input;
}
}
}
return null;
}
function patchSearchBox(searchInput) {
if (searchInput.dataset.patched === 'true') {
log('输入框已打补丁,跳过');
return;
}
log(`修补搜索框: ${searchInput.outerHTML.substring(0, 100)}...`);
searchInput.dataset.patched = 'true';
searchInput.dataset.originalPlaceholder = searchInput.placeholder || '搜索';
searchInput.removeEventListener('keydown', handleSearchKeyDown);
searchInput.addEventListener('keydown', handleSearchKeyDown);
addContextMenu(searchInput);
updatePlaceholder(searchInput);
if (searchHistory.length > 0) {
addSearchHistoryDropdown(searchInput);
}
log('搜索框修补完成');
addStyles();
}
function updatePlaceholder(searchInput) {
const originalPlaceholder = searchInput.dataset.originalPlaceholder || '搜索';
const engineName = CONFIG.searchEngine === 'bing' ? '必应' :
CONFIG.searchEngine === 'baidu' ? '百度' : '谷歌';
searchInput.placeholder = `${originalPlaceholder}(回车使用${engineName}站内搜索)`;
}
function handleSearchKeyDown(event) {
if (event.key === 'Enter' || event.keyCode === 13) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
const searchText = event.target.value.trim();
if (!searchText) {
showNotification('请输入搜索内容', 'warning');
return;
}
performSearch(searchText);
}
}
function performSearch(searchText) {
log(`执行搜索: ${searchText}`);
saveToHistory(searchText);
let query = searchText.trim();
if (CONFIG.autoAddSiteRestriction && query) {
if (!/site\s*[:=]/i.test(query)) {
query += ` ${CONFIG.siteRestriction}`;
}
}
const encodedQuery = encodeURIComponent(query);
const engine = CONFIG.searchEngine.toLowerCase();
const engineTemplate = CONFIG.searchEngines[engine] || CONFIG.searchEngines.bing;
const searchUrl = engineTemplate.replace('{query}', encodedQuery);
log(`搜索URL: ${searchUrl}`);
if (typeof GM_openInTab === 'function') {
GM_openInTab(searchUrl, { active: true, insert: true });
} else {
window.open(searchUrl, '_blank');
}
const engineName = CONFIG.searchEngine === 'bing' ? '必应' :
CONFIG.searchEngine === 'baidu' ? '百度' : '谷歌';
showNotification(`正在使用${engineName}站内搜索: ${searchText}`, 'info');
}
function saveToHistory(searchText) {
searchHistory = searchHistory.filter(item => item !== searchText);
searchHistory.unshift(searchText);
if (searchHistory.length > CONFIG.historySize) {
searchHistory = searchHistory.slice(0, CONFIG.historySize);
}
try {
GM_setValue('searchHistory', JSON.stringify(searchHistory));
} catch (e) {
log('保存历史记录失败: ' + e.message);
}
}
function loadSearchHistory() {
try {
const historyStr = GM_getValue('searchHistory', '[]');
searchHistory = JSON.parse(historyStr) || [];
log(`加载搜索历史: ${searchHistory.length} 条记录`);
} catch (e) {
searchHistory = [];
log('加载搜索历史失败: ' + e.message);
}
}
function addSearchHistoryDropdown(searchInput) {
const container = document.createElement('div');
container.className = 'search-fix-history-container';
container.style.cssText = `
position: absolute;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.5);
max-height: 200px;
overflow-y: auto;
display: none;
z-index: 10000;
min-width: 200px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
`;
let parent = searchInput.parentNode;
if (getComputedStyle(parent).position === 'static') {
parent.style.position = 'relative';
}
parent.appendChild(container);
function updateHistoryList() {
container.innerHTML = '';
if (searchHistory.length === 0) {
const emptyItem = document.createElement('div');
emptyItem.textContent = '无搜索历史';
emptyItem.style.padding = '8px';
emptyItem.style.color = '#666';
emptyItem.style.fontSize = '12px';
emptyItem.style.textAlign = 'center';
container.appendChild(emptyItem);
return;
}
const title = document.createElement('div');
title.textContent = '搜索历史';
title.style.padding = '6px 12px';
title.style.fontWeight = 'bold';
title.style.fontSize = '12px';
title.style.borderBottom = '1px solid #333';
title.style.backgroundColor = '#0d0d0d';
title.style.color = '#999';
container.appendChild(title);
searchHistory.forEach((item, index) => {
const historyItem = document.createElement('div');
historyItem.textContent = item.length > 30 ? item.substring(0, 30) + '...' : item;
historyItem.title = item;
historyItem.style.padding = '6px 12px';
historyItem.style.cursor = 'pointer';
historyItem.style.borderBottom = '1px solid #2a2a2a';
historyItem.style.fontSize = '13px';
historyItem.style.whiteSpace = 'nowrap';
historyItem.style.overflow = 'hidden';
historyItem.style.textOverflow = 'ellipsis';
historyItem.style.color = '#ccc';
historyItem.addEventListener('mouseenter', () => {
historyItem.style.backgroundColor = '#2a2a2a';
});
historyItem.addEventListener('mouseleave', () => {
historyItem.style.backgroundColor = '';
});
historyItem.addEventListener('click', (e) => {
e.stopPropagation();
searchInput.value = item;
container.style.display = 'none';
searchInput.focus();
});
container.appendChild(historyItem);
});
if (searchHistory.length > 0) {
const clearBtn = document.createElement('div');
clearBtn.textContent = '清空历史';
clearBtn.style.padding = '6px 12px';
clearBtn.style.cursor = 'pointer';
clearBtn.style.color = '#ff4d4f';
clearBtn.style.borderTop = '1px solid #333';
clearBtn.style.textAlign = 'center';
clearBtn.style.fontSize = '12px';
clearBtn.style.fontWeight = '500';
clearBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (confirm('确定要清空所有搜索历史吗?')) {
searchHistory = [];
GM_setValue('searchHistory', '[]');
container.style.display = 'none';
showNotification('搜索历史已清空', 'info');
}
});
container.appendChild(clearBtn);
}
}
searchInput.addEventListener('focus', (e) => {
e.stopPropagation();
updateHistoryList();
const rect = searchInput.getBoundingClientRect();
const parentRect = parent.getBoundingClientRect();
container.style.top = (rect.bottom - parentRect.top) + 'px';
container.style.left = (rect.left - parentRect.left) + 'px';
container.style.width = rect.width + 'px';
container.style.display = 'block';
});
searchInput.addEventListener('blur', () => {
setTimeout(() => {
if (!container.matches(':hover')) {
container.style.display = 'none';
}
}, 200);
});
container.addEventListener('mouseenter', () => {
container.style.display = 'block';
});
container.addEventListener('mouseleave', () => {
setTimeout(() => {
if (document.activeElement !== searchInput) {
container.style.display = 'none';
}
}, 100);
});
document.addEventListener('click', (e) => {
if (!container.contains(e.target) && e.target !== searchInput) {
container.style.display = 'none';
}
});
}
function addContextMenu(searchInput) {
searchInput.addEventListener('contextmenu', (e) => {
e.preventDefault();
const menu = document.createElement('div');
menu.className = 'search-fix-context-menu';
menu.style.cssText = `
position: fixed;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.5);
z-index: 10001;
min-width: 180px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
`;
menu.style.left = e.pageX + 'px';
menu.style.top = e.pageY + 'px';
const items = [
{ text: '使用百度搜索', action: () => searchWithEngine('baidu') },
{ text: '使用必应搜索', action: () => searchWithEngine('bing') },
{ text: '使用谷歌搜索', action: () => searchWithEngine('google') },
{ type: 'separator' },
{ text: '清空搜索历史', action: clearSearchHistory }
];
items.forEach(item => {
if (item.type === 'separator') {
const separator = document.createElement('div');
separator.style.height = '1px';
separator.style.backgroundColor = '#333';
separator.style.margin = '4px 0';
menu.appendChild(separator);
} else {
const menuItem = document.createElement('div');
menuItem.textContent = item.text;
menuItem.style.padding = '8px 12px';
menuItem.style.cursor = 'pointer';
menuItem.style.fontSize = '13px';
menuItem.style.color = '#ccc';
menuItem.addEventListener('mouseenter', () => {
menuItem.style.backgroundColor = '#2a2a2a';
});
menuItem.addEventListener('mouseleave', () => {
menuItem.style.backgroundColor = '';
});
menuItem.addEventListener('click', (e) => {
e.stopPropagation();
item.action();
document.body.removeChild(menu);
});
menu.appendChild(menuItem);
}
});
document.body.appendChild(menu);
const closeMenu = (e) => {
if (!menu.contains(e.target)) {
document.body.removeChild(menu);
document.removeEventListener('click', closeMenu);
}
};
setTimeout(() => document.addEventListener('click', closeMenu), 100);
});
}
function searchWithEngine(engine) {
const searchInput = document.querySelector('input[data-patched="true"]');
if (searchInput) {
const searchText = searchInput.value.trim();
if (searchText) {
const oldEngine = CONFIG.searchEngine;
CONFIG.searchEngine = engine;
performSearch(searchText);
CONFIG.searchEngine = oldEngine;
updatePlaceholder(searchInput);
} else {
showNotification('请输入搜索内容', 'warning');
}
}
}
function clearSearchHistory() {
if (searchHistory.length > 0) {
if (confirm('确定要清空所有搜索历史吗?')) {
searchHistory = [];
GM_setValue('searchHistory', '[]');
showNotification('搜索历史已清空', 'info');
}
} else {
showNotification('搜索历史已经是空的', 'info');
}
}
function addSettingsMenu() {
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand('切换搜索引擎', () => {
const current = CONFIG.searchEngine === 'bing' ? '必应' :
CONFIG.searchEngine === 'baidu' ? '百度' : '谷歌';
const newEngine = prompt(
`当前: ${current}\n\n请输入新搜索引擎:\n1. bing\n2. baidu\n3. google`,
CONFIG.searchEngine
);
if (newEngine && ['bing','baidu','google'].includes(newEngine.toLowerCase())) {
CONFIG.searchEngine = newEngine.toLowerCase();
document.querySelectorAll('input[data-patched="true"]').forEach(updatePlaceholder);
const name = {bing:'必应',baidu:'百度',google:'谷歌'}[CONFIG.searchEngine];
showNotification(`已切换为${name}`, 'success');
}
});
GM_registerMenuCommand('查看搜索历史', () => {
if (searchHistory.length === 0) {
alert('暂无搜索历史');
} else {
alert(`搜索历史 (${searchHistory.length}条):\n\n` +
searchHistory.map((item,i) => `${i+1}. ${item}`).join('\n'));
}
});
GM_registerMenuCommand('清空搜索历史', clearSearchHistory);
GM_registerMenuCommand('开启/关闭调试模式', () => {
CONFIG.debug = !CONFIG.debug;
showNotification(`调试模式已${CONFIG.debug?'开启':'关闭'}`, 'info');
});
}
}
function showNotification(message, type = 'info') {
if (typeof GM_notification === 'function') {
GM_notification({
text: message,
title: '论坛搜索修复',
timeout: 3000
});
return;
}
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: ${type === 'info' ? '#1890ff' : type === 'success' ? '#52c41a' : type === 'warning' ? '#faad14' : '#ff4d4f'};
color: white;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 9999;
font-size: 14px;
max-width: 300px;
font-family: system-ui, sans-serif;
pointer-events: none;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
}
function observeDOMChanges() {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) {
if (node.tagName === 'INPUT') {
if (isValidSearchBox(node)) {
setTimeout(() => patchSearchBox(node), 100);
}
} else if (node.querySelectorAll) {
node.querySelectorAll('input').forEach(input => {
if (isValidSearchBox(input)) {
setTimeout(() => patchSearchBox(input), 100);
}
});
}
}
});
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
log('DOM变化监听已启用');
}
function addStyles() {
const styleId = 'search-fix-styles';
if (document.getElementById(styleId)) return;
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
input[data-patched="true"] {
background-color: #1a1a1a !important;
color: #ccc !important;
border-color: #40a9ff !important;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2) !important;
transition: all 0.3s !important;
}
input[data-patched="true"]:focus {
outline: none !important;
border-color: #1890ff !important;
box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.2) !important;
}
input[data-patched="true"]::placeholder {
color: #888 !important;
}
.search-fix-history-container {
scrollbar-width: thin;
scrollbar-color: #444 #1a1a1a;
}
.search-fix-history-container::-webkit-scrollbar {
width: 6px;
}
.search-fix-history-container::-webkit-scrollbar-track {
background: #1a1a1a;
}
.search-fix-history-container::-webkit-scrollbar-thumb {
background: #444;
border-radius: 3px;
}
.search-fix-history-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
`;
document.head.appendChild(style);
log('样式已添加');
}
function log(message) {
if (CONFIG.debug) {
console.log(`[论坛搜索修复] ${message}`);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
您可以在任一支持油猴脚本的加载器中加载并任意修改它,也可以在
智教联盟论坛搜索修复v1.7-脚本猫
进行下载
智教联盟论坛搜索修复v1.8-脚本猫
呃这个是目前最新的版本,v1.7就不删了懒得删(骗你的其实是不会)
版本号乱写的,反正是能用了,有问题的欢迎反馈😃
特别鸣谢:Mux_Yang
我草你知道大半夜人家还开个电脑帮我调试多辛苦你知道吗你知道吗你知道吗