您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
标红你关注的用户
// ==UserScript== // @name NGA 用户标红 // @name:en NGA User Highlighter // @namespace https://github.com/eep // @version 1.0.0 // @description 标红你关注的用户 // @description:en Highlight specific users on NGA forums // @author EEP // @license MIT // @match https://nga.178.com/* // @grant GM_addStyle // @run-at document-end // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // Storage keys const COLOR_CATEGORIES_KEY = 'nga_color_categories'; const USER_CATEGORY_MAP_KEY = 'nga_user_category_map'; // Default categories const DEFAULT_CATEGORIES = { 'red': { name: '网事杂谈', color: '#ffcfcf' }, 'blue': { name: '大时代', color: '#cfdaff' }, 'green': { name: '其他', color: '#cfffcf' } }; // Get color categories from storage function getColorCategories() { const stored = localStorage.getItem(COLOR_CATEGORIES_KEY); return stored ? JSON.parse(stored) : DEFAULT_CATEGORIES; } // Save color categories to storage function saveColorCategories(categories) { localStorage.setItem(COLOR_CATEGORIES_KEY, JSON.stringify(categories)); } // Get user to category mapping function getUserCategoryMap() { const stored = localStorage.getItem(USER_CATEGORY_MAP_KEY); return stored ? JSON.parse(stored) : {}; } // Save user to category mapping function saveUserCategoryMap(userMap) { localStorage.setItem(USER_CATEGORY_MAP_KEY, JSON.stringify(userMap)); } // Add a user to a category function addUserToCategory(userId, categoryId) { const userMap = getUserCategoryMap(); userMap[userId] = categoryId; saveUserCategoryMap(userMap); } // Remove a user from highlighting function removeUser(userId) { const userMap = getUserCategoryMap(); if (userId in userMap) { delete userMap[userId]; saveUserCategoryMap(userMap); } } // Add a new category function addCategory(id, name, color) { const categories = getColorCategories(); categories[id] = { name: name, color: color }; saveColorCategories(categories); } // Remove a category function removeCategory(categoryId) { const categories = getColorCategories(); if (categoryId in categories) { delete categories[categoryId]; saveColorCategories(categories); // Also remove users assigned to this category const userMap = getUserCategoryMap(); Object.entries(userMap).forEach(([userId, catId]) => { if (catId === categoryId) { delete userMap[userId]; } }); saveUserCategoryMap(userMap); } } // Update category properties function updateCategory(categoryId, name, color) { const categories = getColorCategories(); if (categoryId in categories) { categories[categoryId].name = name; categories[categoryId].color = color; saveColorCategories(categories); } } // Function to highlight users in the page function highlightUsers() { const userMap = getUserCategoryMap(); const categories = getColorCategories(); if (Object.keys(userMap).length === 0) return; // Find all user IDs in the page const uidElements = document.querySelectorAll('a[name="uid"]'); uidElements.forEach(element => { // Get the user ID from the text content of the element const userId = element.textContent.trim(); // Check if this user ID is in our highlight list if (userId in userMap) { const categoryId = userMap[userId]; // Check if the category exists if (categoryId in categories) { // Find the parent td element let parent = element; while (parent && parent.tagName !== 'TD') { parent = parent.parentElement; } // Apply highlighting if parent td was found if (parent) { parent.style.backgroundColor = categories[categoryId].color; } } } }); } // Add UI for managing highlighted users function addManagementUI() { // Create a floating button const button = document.createElement('button'); button.textContent = '用户管理'; button.style.position = 'fixed'; button.style.bottom = '20px'; button.style.right = '20px'; button.style.zIndex = '9999'; button.style.padding = '8px'; button.style.backgroundColor = '#f0f0f0'; button.style.border = '1px solid #ccc'; button.style.borderRadius = '4px'; button.style.cursor = 'pointer'; button.addEventListener('click', showMainPanel); document.body.appendChild(button); } function showMainPanel() { // Create panel container const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.top = '50%'; panel.style.left = '50%'; panel.style.transform = 'translate(-50%, -50%)'; panel.style.backgroundColor = 'white'; panel.style.border = '1px solid #ccc'; panel.style.borderRadius = '4px'; panel.style.padding = '20px'; panel.style.zIndex = '10000'; panel.style.minWidth = '400px'; panel.style.maxHeight = '80vh'; panel.style.overflowY = 'auto'; panel.style.overflowX = 'hidden'; panel.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; // Create title const title = document.createElement('h3'); title.textContent = 'NGA用户高亮管理器'; title.style.marginTop = '0'; panel.appendChild(title); // Tab navigation const tabContainer = document.createElement('div'); tabContainer.style.display = 'flex'; tabContainer.style.marginBottom = '15px'; tabContainer.style.borderBottom = '1px solid #ccc'; const createTab = (text, isActive, clickHandler) => { const tab = document.createElement('div'); tab.textContent = text; tab.style.padding = '8px 15px'; tab.style.cursor = 'pointer'; tab.style.backgroundColor = isActive ? '#f0f0f0' : 'transparent'; tab.style.borderBottom = isActive ? '2px solid #0078d7' : 'none'; tab.style.fontWeight = isActive ? 'bold' : 'normal'; tab.addEventListener('click', clickHandler); return tab; }; const contentContainer = document.createElement('div'); // Users tab const usersTab = createTab('用户管理', true, () => { Array.from(tabContainer.children).forEach(tab => { tab.style.backgroundColor = 'transparent'; tab.style.borderBottom = 'none'; tab.style.fontWeight = 'normal'; }); usersTab.style.backgroundColor = '#f0f0f0'; usersTab.style.borderBottom = '2px solid #0078d7'; usersTab.style.fontWeight = 'bold'; contentContainer.innerHTML = ''; showUserManagement(contentContainer); }); // Categories tab const categoriesTab = createTab('分类管理', false, () => { Array.from(tabContainer.children).forEach(tab => { tab.style.backgroundColor = 'transparent'; tab.style.borderBottom = 'none'; tab.style.fontWeight = 'normal'; }); categoriesTab.style.backgroundColor = '#f0f0f0'; categoriesTab.style.borderBottom = '2px solid #0078d7'; categoriesTab.style.fontWeight = 'bold'; contentContainer.innerHTML = ''; showCategoryManagement(contentContainer); }); tabContainer.appendChild(usersTab); tabContainer.appendChild(categoriesTab); panel.appendChild(tabContainer); panel.appendChild(contentContainer); // Initialize with user management tab showUserManagement(contentContainer); // Close button const closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.marginTop = '15px'; closeButton.style.display = 'block'; closeButton.style.width = '100%'; closeButton.style.padding = '8px'; closeButton.addEventListener('click', function() { panel.remove(); }); panel.appendChild(closeButton); document.body.appendChild(panel); } function showUserManagement(container) { const userMap = getUserCategoryMap(); const categories = getColorCategories(); // Create user list const userListContainer = document.createElement('div'); userListContainer.style.marginBottom = '20px'; const userListTitle = document.createElement('h4'); userListTitle.textContent = '已添加用户'; userListTitle.style.marginBottom = '10px'; userListContainer.appendChild(userListTitle); if (Object.keys(userMap).length === 0) { const emptyMessage = document.createElement('p'); emptyMessage.textContent = '暂无添加用户。'; emptyMessage.style.fontStyle = 'italic'; emptyMessage.style.color = '#666'; userListContainer.appendChild(emptyMessage); } else { // Sort users by category for better organization const usersByCategory = {}; Object.entries(userMap).forEach(([userId, categoryId]) => { if (!usersByCategory[categoryId]) { usersByCategory[categoryId] = []; } usersByCategory[categoryId].push(userId); }); Object.entries(usersByCategory).forEach(([categoryId, userIds]) => { const categoryExists = categories[categoryId] !== undefined; const categoryName = categoryExists ? categories[categoryId].name : '未知分类'; const categoryColor = categoryExists ? categories[categoryId].color : '#cccccc'; const categoryHeader = document.createElement('div'); categoryHeader.style.padding = '5px 10px'; categoryHeader.style.backgroundColor = categoryColor; categoryHeader.style.borderRadius = '4px'; categoryHeader.style.marginBottom = '8px'; categoryHeader.style.marginTop = '12px'; categoryHeader.style.fontWeight = 'bold'; categoryHeader.textContent = categoryName; userListContainer.appendChild(categoryHeader); userIds.forEach(userId => { const userItem = document.createElement('div'); userItem.style.padding = '5px 10px'; userItem.style.margin = '5px 0'; userItem.style.display = 'flex'; userItem.style.justifyContent = 'space-between'; userItem.style.alignItems = 'center'; userItem.style.backgroundColor = '#f9f9f9'; userItem.style.borderRadius = '4px'; const userIdText = document.createElement('span'); userIdText.textContent = userId; const actionContainer = document.createElement('div'); // Category selector const categorySelect = document.createElement('select'); categorySelect.style.marginRight = '10px'; Object.entries(categories).forEach(([catId, category]) => { const option = document.createElement('option'); option.value = catId; option.textContent = category.name; option.selected = catId === categoryId; categorySelect.appendChild(option); }); categorySelect.addEventListener('change', function() { addUserToCategory(userId, this.value); container.innerHTML = ''; showUserManagement(container); highlightUsers(); }); // Remove button const removeButton = document.createElement('button'); removeButton.textContent = '删除'; removeButton.addEventListener('click', function() { removeUser(userId); container.innerHTML = ''; showUserManagement(container); highlightUsers(); }); actionContainer.appendChild(categorySelect); actionContainer.appendChild(removeButton); userItem.appendChild(userIdText); userItem.appendChild(actionContainer); userListContainer.appendChild(userItem); }); }); } container.appendChild(userListContainer); // Add new user form const addUserContainer = document.createElement('div'); addUserContainer.style.borderTop = '1px solid #eee'; addUserContainer.style.paddingTop = '15px'; const addUserTitle = document.createElement('h4'); addUserTitle.textContent = '添加新用户'; addUserTitle.style.marginBottom = '10px'; addUserContainer.appendChild(addUserTitle); const form = document.createElement('div'); form.style.display = 'flex'; form.style.alignItems = 'center'; form.style.marginBottom = '10px'; const userIdInput = document.createElement('input'); userIdInput.setAttribute('type', 'text'); userIdInput.setAttribute('placeholder', '输入用户ID'); userIdInput.style.flexGrow = '1'; userIdInput.style.padding = '8px'; userIdInput.style.marginRight = '10px'; const categorySelect = document.createElement('select'); categorySelect.style.padding = '8px'; categorySelect.style.marginRight = '10px'; if (Object.keys(categories).length === 0) { const option = document.createElement('option'); option.value = ''; option.textContent = '请先添加分类'; option.disabled = true; option.selected = true; categorySelect.appendChild(option); } else { Object.entries(categories).forEach(([catId, category]) => { const option = document.createElement('option'); option.value = catId; option.textContent = category.name; categorySelect.appendChild(option); }); } const addButton = document.createElement('button'); addButton.textContent = '添加'; addButton.disabled = Object.keys(categories).length === 0; addButton.addEventListener('click', function() { const userId = userIdInput.value.trim(); const categoryId = categorySelect.value; if (userId && categoryId) { addUserToCategory(userId, categoryId); userIdInput.value = ''; container.innerHTML = ''; showUserManagement(container); highlightUsers(); } }); form.appendChild(userIdInput); form.appendChild(categorySelect); form.appendChild(addButton); addUserContainer.appendChild(form); container.appendChild(addUserContainer); } function showCategoryManagement(container) { const categories = getColorCategories(); // Create category list const categoryListContainer = document.createElement('div'); categoryListContainer.style.marginBottom = '20px'; const categoryListTitle = document.createElement('h4'); categoryListTitle.textContent = '颜色分类'; categoryListTitle.style.marginBottom = '10px'; categoryListContainer.appendChild(categoryListTitle); if (Object.keys(categories).length === 0) { const emptyMessage = document.createElement('p'); emptyMessage.textContent = '暂无颜色分类。'; emptyMessage.style.fontStyle = 'italic'; emptyMessage.style.color = '#666'; categoryListContainer.appendChild(emptyMessage); } else { Object.entries(categories).forEach(([categoryId, category]) => { const categoryItem = document.createElement('div'); categoryItem.style.padding = '12px'; categoryItem.style.margin = '10px 0'; categoryItem.style.display = 'flex'; categoryItem.style.justifyContent = 'space-between'; categoryItem.style.alignItems = 'center'; categoryItem.style.backgroundColor = category.color; categoryItem.style.borderRadius = '4px'; categoryItem.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.1)'; const categoryInfo = document.createElement('div'); categoryInfo.style.display = 'flex'; categoryInfo.style.flexDirection = 'column'; const nameContainer = document.createElement('div'); nameContainer.style.fontWeight = 'bold'; nameContainer.style.marginBottom = '5px'; const nameLabel = document.createElement('span'); nameLabel.textContent = '分类名称: '; nameLabel.style.marginRight = '5px'; const nameInput = document.createElement('input'); nameInput.type = 'text'; nameInput.value = category.name; nameInput.style.border = '1px solid #ccc'; nameInput.style.padding = '3px'; nameInput.style.borderRadius = '2px'; nameContainer.appendChild(nameLabel); nameContainer.appendChild(nameInput); const idContainer = document.createElement('div'); idContainer.style.fontSize = '0.9em'; idContainer.style.color = '#333'; idContainer.textContent = `ID: ${categoryId}`; categoryInfo.appendChild(nameContainer); categoryInfo.appendChild(idContainer); const actionContainer = document.createElement('div'); actionContainer.style.display = 'flex'; actionContainer.style.alignItems = 'center'; const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = category.color; colorInput.style.marginRight = '10px'; colorInput.style.border = 'none'; colorInput.style.width = '30px'; colorInput.style.height = '30px'; colorInput.style.padding = '0'; colorInput.style.cursor = 'pointer'; const saveButton = document.createElement('button'); saveButton.textContent = '保存'; saveButton.style.marginRight = '5px'; saveButton.addEventListener('click', function() { updateCategory(categoryId, nameInput.value, colorInput.value); container.innerHTML = ''; showCategoryManagement(container); highlightUsers(); // Refresh highlighting }); const removeButton = document.createElement('button'); removeButton.textContent = '删除'; removeButton.addEventListener('click', function() { if (confirm(`确定要删除分类 "${category.name}" 吗?这将同时删除所有关联的用户高亮。`)) { removeCategory(categoryId); container.innerHTML = ''; showCategoryManagement(container); highlightUsers(); // Refresh highlighting } }); actionContainer.appendChild(colorInput); actionContainer.appendChild(saveButton); actionContainer.appendChild(removeButton); categoryItem.appendChild(categoryInfo); categoryItem.appendChild(actionContainer); categoryListContainer.appendChild(categoryItem); }); } container.appendChild(categoryListContainer); // Add new category form const addCategoryContainer = document.createElement('div'); addCategoryContainer.style.borderTop = '1px solid #eee'; addCategoryContainer.style.paddingTop = '15px'; const addCategoryTitle = document.createElement('h4'); addCategoryTitle.textContent = '添加新分类'; addCategoryTitle.style.marginBottom = '10px'; addCategoryContainer.appendChild(addCategoryTitle); const form = document.createElement('div'); form.style.display = 'grid'; form.style.gridTemplateColumns = '1fr 1fr'; form.style.gridGap = '10px'; form.style.alignItems = 'center'; const idInput = document.createElement('input'); idInput.setAttribute('type', 'text'); idInput.setAttribute('placeholder', '分类ID(英文/数字)'); idInput.style.padding = '8px'; const nameInput = document.createElement('input'); nameInput.setAttribute('type', 'text'); nameInput.setAttribute('placeholder', '分类名称'); nameInput.style.padding = '8px'; const colorInput = document.createElement('input'); colorInput.setAttribute('type', 'color'); colorInput.value = '#ffcfcf'; colorInput.style.width = '100%'; const addButton = document.createElement('button'); addButton.textContent = '添加分类'; addButton.style.gridColumn = '1 / span 2'; addButton.style.padding = '8px'; addButton.addEventListener('click', function() { const id = idInput.value.trim(); const name = nameInput.value.trim(); const color = colorInput.value; if (id && name) { addCategory(id, name, color); idInput.value = ''; nameInput.value = ''; container.innerHTML = ''; showCategoryManagement(container); } }); form.appendChild(idInput); form.appendChild(nameInput); form.appendChild(colorInput); form.appendChild(addButton); addCategoryContainer.appendChild(form); container.appendChild(addCategoryContainer); } // Run when the page loads function init() { // Convert old format to new format if needed const migrateFromOldFormat = () => { const oldUserData = localStorage.getItem('nga_highlighted_users'); if (oldUserData) { try { const data = JSON.parse(oldUserData); // If old data is an array if (Array.isArray(data)) { const userMap = {}; data.forEach(userId => { userMap[userId] = 'green'; // Default to green category }); saveUserCategoryMap(userMap); } // If old data is an object mapping user IDs to colors else if (typeof data === 'object') { const userMap = {}; const categories = getColorCategories(); // Create categories based on unique colors const uniqueColors = [...new Set(Object.values(data))]; uniqueColors.forEach((color, index) => { const categoryId = `migrated_${index}`; categories[categoryId] = { name: `迁移颜色 ${index + 1}`, color: color }; // Assign users to this category Object.entries(data).forEach(([userId, userColor]) => { if (userColor === color) { userMap[userId] = categoryId; } }); }); saveColorCategories(categories); saveUserCategoryMap(userMap); } // Remove old data format localStorage.removeItem('nga_highlighted_users'); } catch (e) { console.error("Error migrating old data format", e); } } }; migrateFromOldFormat(); highlightUsers(); addManagementUI(); // Add mutation observer to handle dynamically loaded content const observer = new MutationObserver(function(mutations) { highlightUsers(); }); observer.observe(document.body, { childList: true, subtree: true }); } // Run the script after the page loads window.addEventListener('load', init); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址