Old Reddit Karma & Account Age Display Next To Username

Shows karma counts and account age next to Reddit usernames

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Old Reddit Karma & Account Age Display Next To Username
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Shows karma counts and account age next to Reddit usernames
// @author       greenwenvy
// @match        https://www.reddit.com/*
// @match        https://old.reddit.com/*
// @grant        GM_xmlhttpRequest
// @connect      reddit.com
// @license      WTFPL
// ==/UserScript==

(function() {
    'use strict';

    // Determine if we're on old Reddit
    const isOldReddit = window.location.hostname === 'old.reddit.com';

    // Cache to avoid duplicate requests
    const userDataCache = {};

    // Function to get color based on account age
    function getAgeColor(createdDate) {
        const now = new Date();
        const created = new Date(createdDate * 1000);
        const ageInDays = Math.floor((now - created) / (1000 * 60 * 60 * 24));

        // Color scheme based on account age
        if (ageInDays < 30) {
            return '#e74c3c'; // Red for new accounts (< 1 month)
        } else if (ageInDays < 180) {
            return '#e67e22'; // Orange for newer accounts (< 6 months)
        } else if (ageInDays < 365) {
            return '#f1c40f'; // Yellow for accounts < 1 year
        } else if (ageInDays < 365 * 2) {
            return '#27ae60'; // Green for accounts 1-2 years
        } else if (ageInDays < 365 * 5) {
            return '#3498db'; // Blue for accounts 2-5 years
        } else {
            return '#8e44ad'; // Purple for accounts > 5 years
        }
    }

    // Function to format the date difference
    function formatTimeDifference(createdDate) {
        const now = new Date();
        const created = new Date(createdDate * 1000);
        const diffYears = now.getFullYear() - created.getFullYear();
        const diffMonths = now.getMonth() - created.getMonth();

        if (diffYears > 0) {
            return diffYears + (diffYears === 1 ? 'y' : 'y');
        } else if (diffMonths > 0) {
            return diffMonths + (diffMonths === 1 ? 'm' : 'm');
        } else {
            const diffDays = Math.floor((now - created) / (1000 * 60 * 60 * 24));
            return diffDays + (diffDays === 1 ? 'd' : 'd');
        }
    }

    // Function to fetch user data
    function fetchUserData(username) {
        // Return cached data if available
        if (userDataCache[username]) {
            return Promise.resolve(userDataCache[username]);
        }

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://www.reddit.com/user/${username}/about.json`,
                responseType: 'json',
                onload: function(response) {
                    if (response.status === 200 && response.response && response.response.data) {
                        // Cache the data
                        userDataCache[username] = response.response.data;
                        resolve(response.response.data);
                    } else {
                        reject('Failed to fetch user data');
                    }
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    // Function to add karma display
    function addKarmaDisplay(userElement, userData) {
        // Check if we've already added karma info to this element
        if (userElement.getAttribute('data-karma-added') === 'true') {
            return;
        }

        // Create the karma display element
        const karmaDisplay = document.createElement('span');
        karmaDisplay.className = 'karma-display';
        karmaDisplay.style.fontSize = '10px'; // Smaller font
        karmaDisplay.style.color = '#888';
        karmaDisplay.style.marginLeft = '4px';

        // Format the karma numbers to be more compact
        const postKarma = userData.link_karma >= 1000 ?
            (userData.link_karma / 1000).toFixed(1) + 'k' :
            userData.link_karma;

        const commentKarma = userData.comment_karma >= 1000 ?
            (userData.comment_karma / 1000).toFixed(1) + 'k' :
            userData.comment_karma;

        // Create the display text without account age first
        let displayText = `(${postKarma}|${commentKarma})`;
        karmaDisplay.innerHTML = displayText;

        // Add account age for old Reddit with color
        if (isOldReddit) {
            const accountAge = formatTimeDifference(userData.created);
            const ageSpan = document.createElement('span');
            ageSpan.textContent = `|${accountAge}`;
            ageSpan.style.color = getAgeColor(userData.created);

            karmaDisplay.innerHTML = `(${postKarma}|${commentKarma}`;
            karmaDisplay.appendChild(ageSpan);
            karmaDisplay.innerHTML += ')';
        }

        // Insert the karma display after the username
        userElement.parentNode.insertBefore(karmaDisplay, userElement.nextSibling);

        // Mark this element as processed
        userElement.setAttribute('data-karma-added', 'true');
    }

    // Main function to find and process usernames
    function processUsernames() {
        // Selectors for both old and new Reddit
        const userSelectors = isOldReddit ?
            '.author:not([data-karma-added="true"]):not([data-karma-added="processing"])' :
            'a[href^="/user/"]:not([data-karma-added="true"]):not([data-karma-added="processing"]):not([href*="/comments/"]):not([href*="/submit/"])';

        const userElements = document.querySelectorAll(userSelectors);

        userElements.forEach(userElement => {
            // Skip already processed elements
            if (userElement.getAttribute('data-karma-added') === 'true' ||
                userElement.getAttribute('data-karma-added') === 'processing') {
                return;
            }

            const username = userElement.textContent.trim();

            // Skip if username is empty, contains special characters, or is a system user
            if (!username ||
                username.includes('[') ||
                username.includes('deleted') ||
                username === 'AutoModerator') {
                userElement.setAttribute('data-karma-added', 'true'); // Mark as processed
                return;
            }

            // Mark as being processed to prevent duplicate processing
            userElement.setAttribute('data-karma-added', 'processing');

            fetchUserData(username)
                .then(userData => {
                    addKarmaDisplay(userElement, userData);
                })
                .catch(error => {
                    console.error('Error fetching data for', username, error);
                    // Mark as processed even on error to prevent retries
                    userElement.setAttribute('data-karma-added', 'true');
                });
        });
    }

    // Throttle function to limit how often processUsernames runs
    function throttle(func, limit) {
        let inThrottle;
        return function() {
            const args = arguments;
            const context = this;
            if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    }

    // Throttled version of processUsernames
    const throttledProcessUsernames = throttle(processUsernames, 1000);

    // Run on page load after a short delay
    setTimeout(processUsernames, 1000);

    // Set up a mutation observer to handle dynamically loaded content
    const observer = new MutationObserver(function(mutations) {
        // Check if any of the mutations might have added new user links
        let shouldProcess = false;
        for (let mutation of mutations) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                shouldProcess = true;
                break;
            }
        }

        if (shouldProcess) {
            throttledProcessUsernames();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();