Linux do Level Enhanced

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.

La data de 10-03-2024. Vezi ultima versiune.

// ==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();
})();