您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Enhanced script to track progress towards next trust level on linux.do with added search functionality, adjusted posts read limit, and a breathing icon animation.
当前为
// ==UserScript== // @name Linux do Level Enhanced // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description Enhanced script to track progress towards next trust level on linux.do with added search functionality, adjusted posts read limit, and a breathing icon animation. // @author Hua, Reno // @match https://linux.do/* // @icon https://www.google.com/s2/favicons?domain=linux.do // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; const StyleManager = { styles: ` @keyframes breathAnimation { 0%, 100% { transform: scale(1); box-shadow: 0 0 5px rgba(0,0,0,0.5); } 50% { transform: scale(1.1); box-shadow: 0 0 10px rgba(0,0,0,0.7); } } .breath-animation { animation: breathAnimation 4s ease-in-out infinite; } .minimized { border-radius: 50%; cursor: pointer; } .linuxDoLevelPopup { position: fixed; width: 250px; height: 150px; background: var(--d-sidebar-background); box-shadow: 0 0 10px rgba(0,0,0,0.5); padding: 15px; z-index: 10000; font-size: 14px; border-radius: 5px; cursor: move; } .linuxDoLevelPopup input, .linuxDoLevelPopup button { width: 100%; margin-top: 10px; } .linuxDoLevelPopup button { cursor: pointer; } .minimizeButton { position: absolute; top: 5px; right: 5px; background: transparent; border: none; cursor: pointer; width: 30px; height: 30px; font-size: 16px; } .searchButton { width: 100%; marginTop: 10px } .searchBox { width: 100%; marginTop: 10px } `, injectStyles: function() { const styleSheet = document.createElement('style'); styleSheet.type = 'text/css'; styleSheet.innerText = this.styles; document.head.appendChild(styleSheet); } }; const DataManager = { ABOUT_DATA_URL: 'https://linux.do/about.json', USER_DATA_URL_PREFIX: 'https://linux.do/u/', USER_DATA_URL_SUFFIX: '/summary.json', FETCH_REQUEST_OPTIONS: { headers: { "Accept": "application/json", "User-Agent": "Mozilla/5.0" }, method: "GET", }, levelRequirements: { 0: { 'topics_entered': 5, 'posts_read_count': 30, 'time_read': 600 }, 1: { 'days_visited': 15, 'likes_given': 1, 'likes_received': 1, 'post_count': 3, 'topics_entered': 20, 'posts_read_count': 100, 'time_read': 3600 }, 2: { 'days_visited': 50, 'likes_given': 30, 'likes_received': 20, 'post_count': 10 } }, levelDescriptions: { 0: "游客", 1: "基本用户", 2: "成员", 3: "活跃用户", 4: "领导者" }, fetchAboutData: async function() { try { const response = await fetch(this.ABOUT_DATA_URL, this.FETCH_REQUEST_OPTIONS); if (!response.ok) throw new Error(`HTTP 错误!状态:${response.status}`); return await response.json(); } catch (error) { UIManager.displayError("获取关于页面数据失败"); throw error; } }, fetchUserData: async function(username) { try { const url = `${this.USER_DATA_URL_PREFIX}${username}${this.USER_DATA_URL_SUFFIX}`; const response = await fetch(url, this.FETCH_REQUEST_OPTIONS); if (!response.ok) throw new Error(`HTTP 错误!状态:${response.status}`); return await response.json(); } catch (error) { UIManager.displayError("获取用户数据失败"); throw error; } } }; const UIManager = { initPopup: function() { this.popup = this.createElement('div', { id: 'linuxDoLevelPopup', class: 'linuxDoLevelPopup' }); this.content = this.createElement('div', { id: 'linuxDoLevelPopupContent' }, '欢迎使用 Linux do 等级增强插件'); this.searchBox = this.createElement('input', { placeholder: '请输入用户名...', type: 'text', class: 'searchBox' }); this.searchButton = this.createElement('button', { class: 'searchButton' }, '搜索'); this.minimizeButton = this.createElement('button', { }, '隐藏'); this.popup.style.bottom = '20px'; // 示例:距离顶部20px this.popup.style.right = '20px'; // 示例:距离左侧20px this.popup.style.width = '250px'; // 初始化宽度 this.popup.style.height = 'auto'; // 高度自适应内容 this.searchButton.classList.add('btn', 'btn-icon-text', 'btn-default') this.minimizeButton.classList.add('btn', 'btn-icon-text', 'btn-default') this.popup.append(this.content, this.searchBox, this.searchButton, this.minimizeButton); document.body.appendChild(this.popup); this.minimizeButton.addEventListener('click', () => this.togglePopupSize()); this.searchButton.addEventListener('click', () => EventHandler.handleSearch()); // 添加输入框的回车键事件监听器 this.searchBox.addEventListener('keypress', (event) => { // 检查是否按下了回车键并且弹窗不处于最小化状态 if (event.key === 'Enter' && !this.popup.classList.contains('minimized')) { EventHandler.handleSearch(); } }); var checkInterval = setInterval(function() { // 查找id为current-user的li元素 var currentUserLi = document.querySelector('#current-user'); // 如果找到了元素 if(currentUserLi) { // 查找该元素下的button var button = currentUserLi.querySelector('button'); // 如果找到了button元素 if(button) { // 获取button的href属性值 var href = button.getAttribute('href'); UIManager.searchBox.value = href.replace('/u/', ''); clearInterval(checkInterval); // 停止检查 // 这里你可以根据需要对href进行进一步操作 } } }, 1000); // 每隔1秒检查一次 }, createElement: function(tag, attributes, text) { const element = document.createElement(tag); for (const attr in attributes) { if (attr === 'class') { element.classList.add(attributes[attr]); } else { element.setAttribute(attr, attributes[attr]); } } if (text) element.textContent = text; return element; }, updatePopupContent: function(userSummary, user, status) { if (!userSummary || !user) return; let content = `<strong>信任等级:</strong>${DataManager.levelDescriptions[user.trust_level]}<br><strong>升级进度:</strong><br>`; const requirements = DataManager.levelRequirements[user.trust_level] || {}; if (user.trust_level === 2) { requirements['posts_read_count'] = Math.min(parseInt(parseInt(status.posts_30_days) / 4), 20000); requirements['topics_entered'] = Math.min(parseInt(parseInt(status.topics_30_days) / 4), 500); } if (user.trust_level === 3) { content += '联系管理员进行py交易以升级到领导者<br>'; } else if (user.trust_level === 4) { content += '您已是最高信任等级<br>'; } else { let summary = summaryRequired(requirements, userSummary, this.translateStat.bind(this)); content += summary; } this.content.innerHTML = content; }, togglePopupSize: function() { if (this.popup.classList.contains('minimized')) { this.popup.classList.remove('minimized'); this.popup.style.width = '250px'; this.popup.style.height = 'auto'; this.content.style.display = 'block'; this.searchBox.style.display = 'block'; this.searchButton.style.display = 'block'; this.minimizeButton.textContent = '隐藏'; this.popup.classList.remove('breath-animation'); } else { this.popup.classList.add('minimized'); this.popup.style.width = '50px'; this.popup.style.height = '50px'; this.content.style.display = 'none'; this.searchBox.style.display = 'none'; this.searchButton.style.display = 'none'; this.minimizeButton.textContent = '展开'; this.popup.classList.add('breath-animation'); } // 确保拖动时更新整个弹窗的位置而不仅是底部 addDraggableFeature(this.popup); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const popupWidth = this.popup.offsetWidth; const popupHeight = this.popup.offsetHeight; const popupTop = parseInt(this.popup.style.top); const popupLeft = parseInt(this.popup.style.left); let newTop = popupTop; let newLeft = popupLeft; if (!this.popup.classList.contains('minimized')) { // 如果不是最小化状态 if (popupTop + popupHeight > windowHeight) { newTop = windowHeight - popupHeight - 25; } if (popupLeft + popupWidth > windowWidth) { newLeft = windowWidth - popupWidth - 20; } if (popupTop < 60) { newTop = 70; } if (popupLeft < 0) { newLeft = 5; } } else { // 如果是最小化状态 if (popupTop - popupHeight < 60) { newTop = 70; } if (popupLeft < 0) { newLeft = 5; } if (popupLeft + popupWidth > windowWidth) { newLeft = windowWidth - popupWidth - 20; } } this.popup.style.top = newTop + 'px'; this.popup.style.left = newLeft + 'px'; }, displayError: function(message) { this.content.innerHTML = `<strong>错误:</strong>${message}`; }, translateStat: function(stat) { const translations = { 'days_visited': '访问天数', 'likes_given': '给出的赞', 'likes_received': '收到的赞', 'post_count': '帖子数量', 'posts_read_count': '阅读的帖子数', 'topics_entered': '进入的主题数', 'time_read': '阅读时间(秒)' }; return translations[stat] || stat; } }; const EventHandler = { handleSearch: async function() { const username = UIManager.searchBox.value.trim(); if (!username) return; try { const aboutData = await DataManager.fetchAboutData(); const userData = await DataManager.fetchUserData(username); if (userData && aboutData) { UIManager.updatePopupContent(userData.user_summary, userData.users[0], aboutData.about.stats); } } catch (error) { console.error(error); // Already handled within data manager functions } }, // 更新拖动状态 handleDragEnd: function() { UIManager.updateDragStatus(true); } }; // 添加用户升级进度总结 function summaryRequired(required, current, translateStat) { let summary = ''; let allMet = true; for (const stat in required) { if (required.hasOwnProperty(stat) && current.hasOwnProperty(stat)) { const reqValue = required[stat]; const curValue = current[stat] || 0; // 使用 || 0 确保未定义的情况下使用0 if (curValue < reqValue) { allMet = false; const diff = reqValue - curValue; summary += `${translateStat(stat)}: <br><span style="color: red;">当前值: ${curValue}, 需求值: ${reqValue}, 差额: ${diff}</span><br>`; } else { // 如果当前值满足或超过了要求值,也打印出来,但使用不同的颜色或提示信息 summary += `${translateStat(stat)}: <br><span style="color: green;">当前值: ${curValue}, 需求值: ${reqValue}, 已合格</span><br>`; } } } if (allMet) { return "恭喜您!所有项次都已达到合格标准。<br>" + summary; } else { return summary; } } // 添加拖动功能 function addDraggableFeature(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const dragMouseDown = function(e) { // 检查事件的目标是否是输入框,按钮或其他可以忽略拖动逻辑的元素 if (e.target.tagName.toUpperCase() === 'INPUT' || e.target.tagName.toUpperCase() === 'TEXTAREA' || e.target.tagName.toUpperCase() === 'BUTTON') { return; // 如果是,则不执行拖动逻辑 } e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; }; const elementDrag = function(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; // 为了避免与拖动冲突,在此移除bottom和right样式 element.style.bottom = ''; element.style.right = ''; }; const closeDragElement = function() { document.onmouseup = null; document.onmousemove = null; // 在拖动结束时更新拖动状态 EventHandler.handleDragEnd(); }; element.onmousedown = dragMouseDown; } const init = () => { StyleManager.injectStyles(); UIManager.initPopup(); addDraggableFeature(document.getElementById('linuxDoLevelPopup')); // 确保已设置该ID UIManager.togglePopupSize(); // 初始最小化 }; init(); })();