// ==UserScript==
// @name NodeSeekSearchHelper
// @license AGPL-3.0
// @namespace http://www.nodeseek.com/
// @version 2025-05-20
// @description Help you search Nodeseek easer.
// @author xykt
// @match https://www.nodeseek.com/search?*
// @match https://nodeseek.com/search?*
// @icon https://www.nodeseek.com/static/image/favicon/android-chrome-192x192.png
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
const categories = [
'全部显示', '日常', '技术', '情报', '测评', '交易', '曝光',
'拼车', '生活', '贴图', '推广', '内版', 'Dev',
'无意义', '沙盒'
];
const style = document.createElement('style');
style.textContent = `
.category-filter-container {
position: fixed;
top: 55px;
right: 20px;
z-index: 9999;
gap: 6px;
padding: 8px;
width: 200px;
display: flex;
flex-direction: column;
background: inherit;
background-color: var(--bg-main-color);
background-image: none;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
font-size: 14px;
}
.filter-row {
display: flex;
align-items: center;
gap: 4px;
}
.filter-label {
white-space: nowrap;
width: 50px;
}
.category-filter {
padding: 4px;
border-radius: 3px;
border: 1px solid #ddd;
width: 100%;
font-size: 14px;
}
.text-filter {
padding: 4px;
border-radius: 3px;
border: 1px solid #ddd;
width: 100%;
font-size: 14px;
box-sizing: border-box;
}
.filter-option {
display: flex;
align-items: center;
gap: 4px;
margin-top: 2px;
}
.blocked-post {
display: none !important;
}
.post-list-item {
transition: opacity 0.3s;
}
.reset-btn {
padding: 4px;
background: #f0f0f0;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
text-align: center;
margin-top: 4px;
font-size: 14px;
width: 94.8%;
}
.reset-btn:hover {
background: #e0e0e0;
}
.award-icon {
width: 14px;
height: 14px;
vertical-align: middle;
}
`;
document.head.appendChild(style);
const filterContainer = document.createElement('div');
filterContainer.className = 'category-filter-container';
const STORAGE_KEY = 'POST_FILTER_SETTINGS';
let currentSettings = GM_getValue(STORAGE_KEY, {
category: '全部显示',
recommendedOnly: false,
authorFilter: '',
titleFilter: '',
excludeFilter: ''
});
let isFirstLoad = true;
const lastUrl = GM_getValue('LAST_MATCHED_URL', '');
const currentUrl = window.location.href;
if (!currentUrl.startsWith(lastUrl.split('?')[0])) {
currentSettings = {
category: '全部显示',
recommendedOnly: false,
authorFilter: '',
titleFilter: '',
excludeFilter: ''
};
isFirstLoad = true;
}
GM_setValue('LAST_MATCHED_URL', currentUrl);
const categoryRow = document.createElement('div');
categoryRow.className = 'filter-row';
const categoryLabel = document.createElement('label');
categoryLabel.className = 'filter-label';
categoryLabel.textContent = '分类';
categoryLabel.htmlFor = 'categoryFilter';
const select = document.createElement('select');
select.className = 'category-filter';
select.id = 'categoryFilter';
categories.forEach(category => {
const option = document.createElement('option');
option.value = category;
option.textContent = category;
select.appendChild(option);
});
select.value = currentSettings.category;
categoryRow.appendChild(categoryLabel);
categoryRow.appendChild(select);
filterContainer.appendChild(categoryRow);
const titleRow = document.createElement('div');
titleRow.className = 'filter-row';
const titleLabel = document.createElement('label');
titleLabel.className = 'filter-label';
titleLabel.textContent = '标题';
titleLabel.htmlFor = 'titleFilter';
const titleInput = document.createElement('input');
titleInput.type = 'text';
titleInput.className = 'text-filter';
titleInput.id = 'titleFilter';
titleInput.placeholder = ' 包含关键字';
titleInput.value = currentSettings.titleFilter;
titleRow.appendChild(titleLabel);
titleRow.appendChild(titleInput);
filterContainer.appendChild(titleRow);
const excludeRow = document.createElement('div');
excludeRow.className = 'filter-row';
const excludeLabel = document.createElement('label');
excludeLabel.className = 'filter-label';
excludeLabel.textContent = '标题';
excludeLabel.htmlFor = 'excludeFilter';
const excludeInput = document.createElement('input');
excludeInput.type = 'text';
excludeInput.className = 'text-filter';
excludeInput.id = 'excludeFilter';
excludeInput.placeholder = ' 排除关键字';
excludeInput.value = currentSettings.excludeFilter;
excludeRow.appendChild(excludeLabel);
excludeRow.appendChild(excludeInput);
filterContainer.appendChild(excludeRow);
const authorRow = document.createElement('div');
authorRow.className = 'filter-row';
const authorLabel = document.createElement('label');
authorLabel.className = 'filter-label';
authorLabel.textContent = '作者';
authorLabel.htmlFor = 'authorFilter';
const authorInput = document.createElement('input');
authorInput.type = 'text';
authorInput.className = 'text-filter';
authorInput.id = 'authorFilter';
authorInput.placeholder = ' ID / 昵称';
authorInput.value = currentSettings.authorFilter;
authorRow.appendChild(authorLabel);
authorRow.appendChild(authorInput);
filterContainer.appendChild(authorRow);
const recommendedContainer = document.createElement('div');
recommendedContainer.className = 'filter-option';
const recommendedCheckbox = document.createElement('input');
recommendedCheckbox.type = 'checkbox';
recommendedCheckbox.id = 'recommendedOnly';
recommendedCheckbox.checked = currentSettings.recommendedOnly;
const awardIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
awardIcon.setAttribute('class', 'iconpark-icon award award-icon');
awardIcon.setAttribute('style', 'width:14px;height:14px');
const useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use');
useElement.setAttribute('href', '#diamonds');
awardIcon.appendChild(useElement);
const recommendedLabel = document.createElement('label');
recommendedLabel.htmlFor = 'recommendedOnly';
recommendedLabel.appendChild(document.createTextNode('仅显示推荐阅读 '));
recommendedLabel.appendChild(awardIcon);
recommendedContainer.appendChild(recommendedCheckbox);
recommendedContainer.appendChild(recommendedLabel);
filterContainer.appendChild(recommendedContainer);
const resetBtn = document.createElement('div');
resetBtn.className = 'reset-btn';
resetBtn.textContent = '重置筛选';
resetBtn.addEventListener('click', function() {
select.value = '全部显示';
recommendedCheckbox.checked = false;
authorInput.value = '';
titleInput.value = '';
excludeInput.value = '';
saveSettings();
filterPosts();
});
filterContainer.appendChild(resetBtn);
select.addEventListener('change', function() {
saveSettings();
filterPosts();
});
recommendedCheckbox.addEventListener('change', function() {
saveSettings();
filterPosts();
});
authorInput.addEventListener('input', function() {
saveSettings();
filterPosts();
});
titleInput.addEventListener('input', function() {
saveSettings();
filterPosts();
});
excludeInput.addEventListener('input', function() {
saveSettings();
filterPosts();
});
document.body.appendChild(filterContainer);
function saveSettings() {
currentSettings = {
category: select.value,
recommendedOnly: recommendedCheckbox.checked,
authorFilter: authorInput.value.trim(),
titleFilter: titleInput.value.trim(),
excludeFilter: excludeInput.value.trim()
};
GM_setValue(STORAGE_KEY, currentSettings);
GM_setValue('LAST_MATCHED_URL', window.location.href);
}
function filterPosts() {
const selectedCategory = select.value;
const showRecommendedOnly = recommendedCheckbox.checked;
const authorFilterText = authorInput.value.trim().toLowerCase();
const titleFilterText = titleInput.value.trim().toLowerCase();
const excludeFilterText = excludeInput.value.trim().toLowerCase();
document.querySelectorAll('li.post-list-item').forEach(post => {
post.classList.remove('blocked-post');
const categoryElement = post.querySelector('.post-category');
const postCategory = categoryElement ? categoryElement.textContent.trim() : '';
const isRecommended = post.querySelector('a[href="/award"][title="推荐阅读"]') !== null;
const authorLink = post.querySelector('.info-author a');
const authorName = authorLink ? authorLink.textContent.trim().toLowerCase() : '';
const authorImg = post.querySelector('img.avatar-normal');
const authorAlt = authorImg ? authorImg.alt.toLowerCase() : '';
const titleElement = post.querySelector('.post-title a');
const postTitle = titleElement ? titleElement.textContent.trim().toLowerCase() : '';
const categoryMatch =
selectedCategory === '全部显示' ||
selectedCategory === '' ||
postCategory === selectedCategory;
const recommendedMatch = !showRecommendedOnly || isRecommended;
const authorMatch =
authorFilterText === '' ||
authorName.includes(authorFilterText) ||
authorAlt.includes(authorFilterText);
const titleMatch =
titleFilterText === '' ||
postTitle.includes(titleFilterText);
const excludeMatch =
excludeFilterText === '' ||
!postTitle.includes(excludeFilterText);
if (!categoryMatch || !recommendedMatch || !authorMatch || !titleMatch || !excludeMatch) {
post.classList.add('blocked-post');
}
});
}
if (isFirstLoad) {
setTimeout(filterPosts, 500);
} else {
filterPosts();
}
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
filterPosts();
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
window.addEventListener('beforeunload', function() {
GM_setValue('LAST_MATCHED_URL', window.location.href);
});
})();