最初的

Kratos
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. 油猴插件
  3. 正文

NodeLoc抽奖排名油猴插件

2025年9月23日 121点热度 0人点赞 0条评论

NodeLoc上现在是奖券抽奖的,默认会显示个人中奖概率,但实际上抽奖的时候奖品可能不唯一,比如抽奖奖品数量是5,你投入的奖券是10,占总票数的100的10%,那么你实际显示的中奖概率是10%,但是实际上你的第一名投入的奖券比如是50,其余的40人投入的奖券数量的40,那么你投入的10个券其实是排名第二的,你中奖概率实际上是100%。

// ==UserScript==
// @name         NodeLoc抽奖参与者排序插件
// @namespace    http://tampermonkey.net/
// @version      1.01
// @description  在NodeLoc抽奖页面显示按奖券数量排序的参与者列表
// @author       You
// @match        https://www.nodeloc.com/t/topic/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 获取当前登录用户名
    function getCurrentUsername() {
        // 方法1: 从 Discourse 全局对象获取
        if (typeof Discourse !== 'undefined' && Discourse.User!=='undefined') {
            return Discourse.User.current().name;
        }

        // 方法2: 从页面元素获取
        const userMenuTrigger = document.querySelector('.header-dropdown-toggle.current-user');
        if (userMenuTrigger) {
            const img = userMenuTrigger.querySelector('img');
            if (img && img.alt) {
                return img.alt;
            }
        }

        // 方法3: 从用户菜单链接获取
        const userLink = document.querySelector('a[href^="/u/"].current-user');
        if (userLink) {
            const href = userLink.getAttribute('href');
            const match = href.match(/\/u\/([^\/]+)/);
            if (match) {
                return match[1];
            }
        }

        // 方法4: 从页面头部用户信息获取
        const currentUserElement = document.querySelector('.current-user .username');
        if (currentUserElement) {
            return currentUserElement.textContent.trim();
        }

        // 方法5: 从 meta 标签获取
        const currentUserMeta = document.querySelector('meta[name="discourse-current-user"]');
        if (currentUserMeta) {
            try {
                const userInfo = JSON.parse(currentUserMeta.content);
                return userInfo.username;
            } catch (e) {
                console.log('Failed to parse current user meta:', e);
            }
        }

        return null;
    }

    // 等待参与者列表加载完成
    function waitForParticipants(participantList, maxRetries = 20, retryDelay = 500) {
        return new Promise((resolve) => {
            let retryCount = 0;

            function checkParticipants() {
                const participantLinks = participantList.querySelectorAll('a[title]');
                console.log(`尝试 ${retryCount + 1}: 找到 ${participantLinks.length} 个参与者链接`);

                if (participantLinks.length > 0) {
                    // 检查是否有有效的title属性
                    const validLinks = Array.from(participantLinks).filter(link => {
                        const title = link.getAttribute('title');
                        return title && title.includes('奖券');
                    });

                    console.log(`有效的参与者链接: ${validLinks.length}`);

                    if (validLinks.length > 0) {
                        resolve(participantLinks);
                        return;
                    }
                }

                retryCount++;
                if (retryCount < maxRetries) {
                    setTimeout(checkParticipants, retryDelay);
                } else {
                    console.log('达到最大重试次数,继续执行...');
                    resolve(participantLinks);
                }
            }

            checkParticipants();
        });
    }

    // 检查页面是否包含抽奖参与者列表
    async function checkAndAddSortedList() {
        // 首先检查是否是抽奖页面
        const lotteryContainer = document.querySelector('.lottery-container');
        if (!lotteryContainer) {
            console.log('不是抽奖页面,跳过插件逻辑');
            return;
        }

        const participantList = document.querySelector('.lottery-participant-list');
        if (!participantList) {
            console.log('未找到参与者列表');
            return;
        }

        // 检查是否已经添加过排序列表
        if (document.querySelector('.sorted-participant-list')) {
            return;
        }

        console.log('发现抽奖页面,等待参与者内容加载...');

        // 等待参与者链接加载完成
        const participantLinks = await waitForParticipants(participantList);

        // 获取当前登录用户名
        const currentUsername = getCurrentUsername();

        const participants = [];

        // 解析参与者信息
        participantLinks.forEach(link => {
            const title = link.getAttribute('title');
            console.log('解析title:', title); // 调试用

            // 使用正则表达式匹配用户名和奖券数量
            // 支持中文括号()和英文括号()
            const match = title.match(/^(.+?)[((](\d+)\s*奖券[))]$/);
            if (match) {
                const username = match[1].trim();
                const ticketCount = parseInt(match[2]);
                console.log('解析结果:', username, ticketCount); // 调试用
                participants.push({
                    username: username,
                    tickets: ticketCount,
                    href: link.getAttribute('href')
                });
            } else {
                console.log('未能解析的title:', title); // 调试用
            }
        });

        // 如果没有解析到参与者,显示错误信息
        if (participants.length === 0) {
            console.log('未解析到任何参与者');
            const errorDiv = document.createElement('div');
            errorDiv.className = 'sorted-participant-list';
            errorDiv.style.cssText = `
                margin-top: 15px;
                padding: 15px;
                background-color: #fff3cd;
                border: 1px solid #ffeaa7;
                border-radius: 5px;
                font-family: Arial, sans-serif;
                text-align: center;
                color: #856404;
            `;
            errorDiv.innerHTML = `
                <h4 style="margin: 0 0 10px 0;">参与者排行榜</h4>
                <p style="margin: 0;">正在等待抽奖数据加载,请稍后刷新页面重试...</p>
            `;
            participantList.parentNode.insertBefore(errorDiv, participantList.nextSibling);
            return;
        }

        // 按奖券数量从高到低排序
        participants.sort((a, b) => b.tickets - a.tickets);

        // 查找当前用户排名
        let currentUserRank = -1;
        let currentUserTickets = 0;
        if (currentUsername) {
            const userIndex = participants.findIndex(p => p.username.toLowerCase() === currentUsername.toLowerCase());
            if (userIndex !== -1) {
                currentUserRank = userIndex + 1;
                currentUserTickets = participants[userIndex].tickets;
            }
        }

        // 创建排序后的列表容器
        const sortedDiv = document.createElement('div');
        sortedDiv.className = 'sorted-participant-list';
        sortedDiv.style.cssText = `
            margin-top: 15px;
            padding: 15px;
            background-color: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 5px;
            font-family: Arial, sans-serif;
        `;

        // 添加标题
        const titleContainer = document.createElement('div');
        titleContainer.style.cssText = `
            margin-bottom: 10px;
        `;

        const title = document.createElement('h4');
        title.textContent = '参与者排行榜(按奖券数量排序)';
        title.style.cssText = `
            margin: 0 0 10px 0;
            color: #495057;
            font-size: 16px;
            font-weight: bold;
        `;
        titleContainer.appendChild(title);

        // 如果找到当前用户,在标题下方显示排名信息
        if (currentUserRank > 0) {
            const userRankInfo = document.createElement('div');
            userRankInfo.style.cssText = `
                background-color: #007bff;
                color: white;
                padding: 8px 15px;
                border-radius: 20px;
                font-size: 14px;
                font-weight: bold;
                display: inline-block;
                margin-bottom: 10px;
                box-shadow: 0 2px 4px rgba(0,123,255,0.3);
            `;
            userRankInfo.textContent = `我当前排第${currentUserRank}名 (${currentUserTickets}张奖券)`;
            titleContainer.appendChild(userRankInfo);
        } else if (currentUsername) {
            // 如果有用户名但没找到排名,显示未参与信息
            const notParticipatingInfo = document.createElement('div');
            notParticipatingInfo.style.cssText = `
                background-color: #6c757d;
                color: white;
                padding: 8px 15px;
                border-radius: 20px;
                font-size: 14px;
                font-weight: bold;
                display: inline-block;
                margin-bottom: 10px;
                box-shadow: 0 2px 4px rgba(108,117,125,0.3);
            `;
            notParticipatingInfo.textContent = `我未参与此次抽奖`;
            titleContainer.appendChild(notParticipatingInfo);
        }

        sortedDiv.appendChild(titleContainer);

        // 创建列表容器
        const listContainer = document.createElement('div');
        listContainer.style.cssText = `
            max-height: 400px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 3px;
            background-color: white;
        `;

        // 添加参与者列表项
        participants.forEach((participant, index) => {
            const isCurrentUser = currentUsername && participant.username.toLowerCase() === currentUsername.toLowerCase();

            const item = document.createElement('div');
            item.style.cssText = `
                padding: 8px 12px;
                border-bottom: 1px solid #eee;
                display: flex;
                justify-content: space-between;
                align-items: center;
                transition: background-color 0.2s;
                ${isCurrentUser ? 'border-left: 4px solid #007bff; box-shadow: 0 2px 4px rgba(0,123,255,0.2);' : ''}
            `;

            // 设置背景色
            let backgroundColor = 'white';
            if (isCurrentUser) {
                backgroundColor = '#e3f2fd';
            } else if (index === 0) {
                backgroundColor = '#fff3cd';
            } else if (index === 1) {
                backgroundColor = '#f8f9fa';
            } else if (index === 2) {
                backgroundColor = '#f1f3f4';
            }
            item.style.backgroundColor = backgroundColor;

            // 鼠标悬停效果
            item.addEventListener('mouseenter', () => {
                item.style.backgroundColor = isCurrentUser ? '#bbdefb' : '#e9ecef';
            });
            item.addEventListener('mouseleave', () => {
                item.style.backgroundColor = backgroundColor;
            });

            // 创建用户名链接
            const nameLink = document.createElement('a');
            nameLink.href = participant.href;
            nameLink.textContent = participant.username;
            nameLink.style.cssText = `
                color: ${isCurrentUser ? '#1976d2' : '#007bff'};
                text-decoration: none;
                font-weight: ${isCurrentUser ? 'bold' : '500'};
            `;
            nameLink.addEventListener('mouseenter', () => {
                nameLink.style.textDecoration = 'underline';
            });
            nameLink.addEventListener('mouseleave', () => {
                nameLink.style.textDecoration = 'none';
            });

            // 创建奖券数量显示
            const ticketCount = document.createElement('span');
            ticketCount.textContent = `${participant.tickets} 奖券`;
            ticketCount.style.cssText = `
                color: ${isCurrentUser ? '#1976d2' : '#28a745'};
                font-weight: bold;
                background-color: ${isCurrentUser ? '#e3f2fd' : '#d4edda'};
                padding: 2px 8px;
                border-radius: 12px;
                font-size: 12px;
                ${isCurrentUser ? 'border: 1px solid #1976d2;' : ''}
            `;

            // 添加排名
            const rank = document.createElement('span');
            rank.textContent = `#${index + 1}`;
            rank.style.cssText = `
                color: ${isCurrentUser ? '#1976d2' : '#6c757d'};
                font-size: 12px;
                font-weight: bold;
                margin-right: 10px;
                min-width: 30px;
            `;

            // 如果是当前用户,添加标识
            const leftContainer = document.createElement('div');
            leftContainer.style.display = 'flex';
            leftContainer.style.alignItems = 'center';
            leftContainer.appendChild(rank);
            leftContainer.appendChild(nameLink);

            if (isCurrentUser) {
                const meLabel = document.createElement('span');
                meLabel.textContent = ' (我)';
                meLabel.style.cssText = `
                    color: #1976d2;
                    font-size: 11px;
                    font-weight: bold;
                    background-color: #e3f2fd;
                    padding: 1px 5px;
                    border-radius: 8px;
                    margin-left: 5px;
                    border: 1px solid #1976d2;
                `;
                leftContainer.appendChild(meLabel);
            }

            item.appendChild(leftContainer);
            item.appendChild(ticketCount);
            listContainer.appendChild(item);
        });

        sortedDiv.appendChild(listContainer);

        // 添加统计信息
        const stats = document.createElement('div');
        stats.style.cssText = `
            margin-top: 10px;
            font-size: 12px;
            color: #6c757d;
            text-align: center;
        `;
        const totalTickets = participants.reduce((sum, p) => sum + p.tickets, 0);
        stats.textContent = `总计 ${participants.length} 人参与,共 ${totalTickets} 张奖券`;
        sortedDiv.appendChild(stats);

        // 将排序列表插入到原列表下方
        participantList.parentNode.insertBefore(sortedDiv, participantList.nextSibling);
    }

    // 页面加载完成后执行
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(checkAndAddSortedList, 1000); // 延迟1秒执行
        });
    } else {
        setTimeout(checkAndAddSortedList, 1000); // 延迟1秒执行
    }

    // 监听页面变化(处理AJAX加载的情况)
    const observer = new MutationObserver((mutations) => {
        let shouldCheck = false;
        mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === 1) {
                        // 检查是否添加了抽奖容器
                        if (node.classList && node.classList.contains('lottery-container')) {
                            shouldCheck = true;
                        } else if (node.querySelector && node.querySelector('.lottery-container')) {
                            shouldCheck = true;
                        } else if (node.classList && node.classList.contains('lottery-participant-list')) {
                            shouldCheck = true;
                        } else if (node.querySelector && node.querySelector('.lottery-participant-list')) {
                            shouldCheck = true;
                        } else if (node.querySelector && node.querySelector('a[title*="奖券"]')) {
                            // 检测到包含"奖券"的链接被添加
                            shouldCheck = true;
                        }
                    }
                });
            }
        });

        if (shouldCheck) {
            console.log('检测到抽奖相关页面变化,延迟执行检查...');
            setTimeout(checkAndAddSortedList, 500);
        }
    });

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

})();
标签: NodeLoc插件 抽奖插件
最后更新:2025年9月23日

skybreak

这个人很懒,什么都没留下

点赞

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

归档

  • 2025 年 10 月
  • 2025 年 9 月
  • 2025 年 8 月

分类

  • NAT64
  • VPS测评
  • 前端
  • 前端面试题
  • 华为云考试
  • 教程
  • 未分类
  • 油猴插件
  • 纯IPV6
  • 羊毛

COPYRIGHT © 2025 最初的. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang