- // ==UserScript==
- // @name Linux do Level Enhanced
- // @namespace http://tampermonkey.net/
- // @version 0.0.4
- // @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 = {
- injectStyles: function() {
- const styleSheet = document.createElement('style');
- styleSheet.type = 'text/css';
- styleSheet.innerText = `
- @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 {
- width: 50px !important;
- height: 50px !important;
- border-radius: 50% !important;
- padding: 0 !important;
- overflow: hidden;
- cursor: pointer;
- }
- .button:hover {
- background-color: #f0f0f0;
- }
- `;
- 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, 'topics_entered': 0, 'posts_read_count': 0 }
- },
- levelDescriptions: {
- 0: "游客", 1: "基本用户", 2: "成员", 3: "活跃用户", 4: "领导者"
- },
-
- async fetchAboutData() {
- 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) {
- console.error("获取关于页面数据失败:", error);
- UIManager.displayError("获取关于页面数据失败");
- return null;
- }
- },
-
- async fetchUserData(username) {
- try {
- const response = await fetch(`${this.USER_DATA_URL_PREFIX}${username}${this.USER_DATA_URL_SUFFIX}`, this.FETCH_REQUEST_OPTIONS);
- if (!response.ok) throw new Error(`HTTP 错误!状态:${response.status}`);
- return await response.json();
- } catch (error) {
- console.error("获取用户数据失败:", error);
- UIManager.displayError("获取用户数据失败");
- return null;
- }
- }
- };
-
- const UIManager = {
- popup: null,
- content: null,
- searchBox: null,
- searchButton: null,
- minimizeButton: null,
-
- createPopup: function() {
- this.popup = document.createElement('div');
- this.popup.classList.add('linuxDoLevelPopup');
- this.popup.id = 'linuxDoLevelPopup';
- this.setStyle(this.popup, {
- position: 'fixed',
- bottom: '20px',
- right: '20px',
- width: '250px',
- height: 'auto',
- backgroundColor: 'white',
- boxShadow: '0 0 10px rgba(0,0,0,0.5)',
- padding: '15px',
- zIndex: '10000',
- fontSize: '14px',
- borderRadius: '5px',
- });
-
- this.content = document.createElement('div');
- this.content.id = 'linuxDoLevelPopupContent';
- this.content.innerHTML = '欢迎使用 Linux do 等级增强插件';
- this.popup.appendChild(this.content);
-
- this.searchBox = document.createElement('input');
- this.searchBox.type = 'text';
- this.searchBox.placeholder = '请输入用户名...';
- this.setStyle(this.searchBox, { width: '100%', marginTop: '10px' });
- this.searchBox.id = 'linuxDoUserSearch';
- this.popup.appendChild(this.searchBox);
-
- this.searchButton = document.createElement('button');
- this.searchButton.textContent = '搜索';
- this.searchButton.classList.add('button');
- this.setStyle(this.searchButton, { width: '100%', marginTop: '10px' });
- this.popup.appendChild(this.searchButton);
-
- this.minimizeButton = document.createElement('button');
- this.minimizeButton.textContent = '隐藏';
- this.minimizeButton.classList.add('button');
- this.setStyle(this.minimizeButton, {
- position: 'absolute',
- top: '5px',
- right: '5px',
- zIndex: '10001',
- background: 'transparent',
- border: 'none',
- cursor: 'pointer',
- borderRadius: '50%',
- textAlign: 'center',
- lineHeight: '40px',
- width: '40px',
- height: '40px',
- });
- this.popup.appendChild(this.minimizeButton);
-
- document.body.appendChild(this.popup);
- },
-
- updatePopupContent: function(userSummary, user, status) {
- if (this.content && userSummary && user) {
- let content = `<strong>信任等级:</strong>${DataManager.levelDescriptions[user.trust_level]}<br><strong>升级进度:</strong><br>`;
-
- if (user.trust_level === 3) {
- content += `联系管理员以升级到领导者<br>`;
- } else if (user.trust_level === 4) {
- content += `您已是最高信任等级<br>`;
- } else {
- const requirements = DataManager.levelRequirements[user.trust_level];
- if (user.trust_level === 2) {
- requirements['posts_read_count'] = Math.min(Math.floor(status.posts_30_days / 4), 20000);
- requirements['topics_entered'] = Math.min(Math.floor(status.topics_30_days / 4), 500);
- }
-
- Object.entries(requirements).forEach(([key, val]) => {
- const currentVal = userSummary[key] || 0;
- const color = currentVal >= val ? "green" : "red";
- content += `${this.translateStat(key)}: <span style="color: ${color};">${currentVal} / ${val}</span><br>`;
- });
- }
-
- this.content.innerHTML = content;
- }
- },
-
- togglePopupSize: function() {
- if (this.popup.classList.contains('minimized')) {
- // Maximizing
- this.popup.classList.remove('minimized');
- this.popup.classList.remove('breath-animation');
- 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 = '隐藏';
- } else {
- // Minimizing
- this.popup.classList.add('minimized');
- this.popup.classList.add('breath-animation');
- 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 = '';
- }
- },
-
- displayError: function(message) {
- if (this.content) {
- this.content.innerHTML = `<strong>错误:</strong>${message}`;
- }
- },
-
- setStyle: function(element, styles) {
- Object.assign(element.style, styles);
- },
-
- 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) {
- const aboutData = await DataManager.fetchAboutData();
- const userData = await DataManager.fetchUserData(username);
- if (userData && aboutData) {
- const userSummary = userData.user_summary;
- const user = userData.users[0];
- const status = aboutData.about.stats;
- UIManager.updatePopupContent(userSummary, user, status);
- }
- }
- },
-
- initEventListeners: function() {
- UIManager.searchButton.addEventListener('click', this.handleSearch.bind(this));
- UIManager.minimizeButton.addEventListener('click', UIManager.togglePopupSize.bind(UIManager));
- }
- };
-
- const LevelEnhancement = {
- init: function() {
- StyleManager.injectStyles();
- UIManager.createPopup();
- EventHandler.initEventListeners();
- }
- };
-
- LevelEnhancement.init();
- })();