百度贴吧点赞数据显示

在百度贴吧显示主题帖、楼层的点赞数据

// ==UserScript==
// @name         百度贴吧点赞数据显示
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  在百度贴吧显示主题帖、楼层的点赞数据
// @author       noahacgn
// @match        *://tieba.baidu.com/p/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @connect      tiebac.baidu.com
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 配置信息
    const config = {
        BDUSS: '', // 用户手动填写BDUSS
        CLIENT_INFO: {
            _client_version: "12.64.1.1",
            _client_type: "2"
        },
        BASE_URL: "http://tiebac.baidu.com",
        lastUrl: location.href, // 记录上一次处理的URL
        postDataCache: {}, // 缓存帖子数据
        isProcessing: false // 是否正在处理数据
    };

    // 初始化
    function init() {
        console.log("百度贴吧点赞数据显示脚本初始化...");
        // 从GM存储中获取BDUSS
        config.BDUSS = GM_getValue('BDUSS', '');
        
        // 如果没有BDUSS,则提示用户设置
        if (!config.BDUSS) {
            showBDUSSPrompt();
        } else {
            // 获取当前帖子ID
            const tid = getTidFromUrl();
            if (tid) {
                console.log("获取到帖子ID:", tid);
                // 获取帖子点赞数据
                getPostData(tid);
            } else {
                console.error("无法从URL获取帖子ID");
            }
        }
    }

    // 显示BDUSS设置提示
    function showBDUSSPrompt() {
        const userBDUSS = prompt("请输入您的BDUSS值以获取点赞数据:", "");
        if (userBDUSS) {
            config.BDUSS = userBDUSS;
            GM_setValue('BDUSS', userBDUSS);
            
            // 获取当前帖子ID
            const tid = getTidFromUrl();
            if (tid) {
                // 获取帖子点赞数据
                getPostData(tid);
            }
        }
    }

    // 从URL获取帖子ID
    function getTidFromUrl() {
        const match = window.location.href.match(/p\/(\d+)/);
        return match ? match[1] : null;
    }

    // 获取当前页码
    function getCurrentPage() {
        const match = window.location.href.match(/pn=(\d+)/);
        return match ? parseInt(match[1]) : 1;
    }

    // 加密签名函数
    function generateSign(params) {
        // 先排序参数
        const sortedParams = new URLSearchParams(params);
        sortedParams.sort();
        
        // 构建签名字符串
        const signStr = Array.from(sortedParams.entries())
            .map(entry => entry.join('='))
            .join('') + "tiebaclient!!!";
        
        // 计算MD5
        return md5(signStr).toUpperCase();
    }

    // 打包请求参数,添加签名
    function packRequest(params) {
        const reqParams = { ...params, ...config.CLIENT_INFO };
        
        // 添加BDUSS
        if (!reqParams.BDUSS) {
            reqParams.BDUSS = config.BDUSS;
        }
        
        // 生成签名
        const sign = generateSign(reqParams);
        reqParams.sign = sign;
        
        return new URLSearchParams(reqParams).toString();
    }

    // 使用GM_xmlhttpRequest调用百度贴吧API
    function fetchAPI(url, data, method = "GET") {
        return new Promise((resolve, reject) => {
            let apiUrl = config.BASE_URL + url;
            const fetchOptions = {
                method: method,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const responseData = JSON.parse(response.responseText);
                            resolve(responseData);
                        } catch (error) {
                            reject(new Error("API返回数据解析失败: " + error.message));
                        }
                    } else {
                        reject(new Error(`HTTP错误! 状态: ${response.status}`));
                    }
                },
                onerror: function(error) {
                    reject(new Error("API请求失败: " + error.message));
                }
            };

            // 根据方法添加不同的参数
            if (method === "GET") {
                if (data) {
                    apiUrl += `?${data}`;
                }
            } else if (method === "POST") {
                fetchOptions.data = data;
            }

            GM_xmlhttpRequest({
                ...fetchOptions,
                url: apiUrl
            });
        });
    }

    // 获取帖子数据(包含点赞信息)
    async function getPostData(tid) {
        try {
            if (config.isProcessing) {
                console.log("正在处理数据,稍后再试");
                return;
            }

            config.isProcessing = true;
            const currentPage = getCurrentPage();
            console.log(`开始获取帖子数据,帖子ID: ${tid},页码: ${currentPage}`);
            
            // 构造请求参数
            const params = {
                kz: tid,
                pn: currentPage,
                rn: 30, // 每页回复数量
                r: 3,   // 排序方式: 3为时间正序
                BDUSS: config.BDUSS
            };
            
            // 发起请求
            const data = await fetchAPI("/c/f/pb/page", packRequest(params), "POST");
            
            console.log("获取到帖子数据:", data);
            
            if (!data || data.error) {
                throw new Error(`获取帖子数据失败: ${data?.error_msg || '未知错误'}`);
            }
            
            // 缓存帖子数据
            const cacheKey = `${tid}_${currentPage}`;
            config.postDataCache[cacheKey] = data;
            
            // 使用智能延迟处理显示
            setTimeout(() => {
                processPostData(tid, currentPage);
            }, 500);
            
            return data;
        } catch (error) {
            console.error("获取帖子点赞数据失败:", error);
            config.isProcessing = false;
            return null;
        }
    }

    // 处理帖子数据并显示点赞信息
    function processPostData(tid, page) {
        const cacheKey = `${tid}_${page}`;
        const data = config.postDataCache[cacheKey];
        
        if (!data) {
            console.error("缓存中找不到帖子数据:", cacheKey);
            config.isProcessing = false;
            return;
        }
        
        try {
            // 只在第一页显示主题帖点赞数据
            if (page === 1 && data.thread) {
                displayThreadLikeCount(data.thread);
            }
            
            // 检查页面是否已经加载完成
            const postsLoaded = checkPostsLoaded(data);
            
            if (postsLoaded) {
                console.log("页面已加载完成,显示点赞数据");
                // 显示回复的点赞数据
                if (data.post_list && Array.isArray(data.post_list) && data.post_list.length > 0) {
                    data.post_list.forEach(post => {
                        if (!post) return;
                        displayPostLikeCount(post);
                    });
                }
                
                // 处理完成
                config.isProcessing = false;
            } else {
                console.log("页面尚未加载完成,500ms后重试");
                // 如果页面未加载完,延迟重试
                setTimeout(() => {
                    processPostData(tid, page);
                }, 500);
            }
        } catch (error) {
            console.error("处理帖子数据出错:", error);
            config.isProcessing = false;
        }
    }

    // 检查页面是否加载完成
    function checkPostsLoaded(data) {
        if (!data.post_list || !Array.isArray(data.post_list) || data.post_list.length === 0) {
            return true; // 没有回复,认为加载完成
        }
        
        // 检查页面是否已加载数据中的第一个和最后一个帖子
        const firstPost = data.post_list[0];
        const lastPost = data.post_list[data.post_list.length - 1];
        
        if (!firstPost || !lastPost || !firstPost.id || !lastPost.id) {
            return false;
        }
        
        const firstPostElement = document.querySelector(`.l_post[data-pid="${firstPost.id}"]`);
        const lastPostElement = document.querySelector(`.l_post[data-pid="${lastPost.id}"]`);
        
        return firstPostElement && lastPostElement;
    }

    // 显示主题帖点赞数
    function displayThreadLikeCount(thread) {
        const titleElement = document.querySelector('.core_title_txt.pull-left.text-overflow');
        if (titleElement) {
            const likeCount = thread.agree?.agree_num || 0;
            // 检查是否已经添加了点赞计数
            const existingLikeElement = document.querySelector('.thread-like-count');
            if (existingLikeElement) {
                return;
            }
            
            const likeElement = document.createElement('span');
            likeElement.className = 'thread-like-count';
            likeElement.innerHTML = `<span style="color: #E74C3C; margin-left: 10px;"><i style="font-size: 14px; margin-right: 3px;">❤</i>${likeCount}</span>`;
            titleElement.after(likeElement);
            
            console.log("已显示主题帖点赞数:", likeCount);
        }
    }

    // 显示楼层点赞数
    function displayPostLikeCount(post) {
        if (!post || !post.id) {
            console.warn("无效的回复数据:", post);
            return;
        }
        
        const postElement = document.querySelector(`.l_post[data-pid="${post.id}"]`);
        if (postElement) {
            const tailElement = postElement.querySelector('.core_reply_tail');
            if (tailElement) {
                // 检查是否已经添加了点赞计数
                const existingLikeElement = tailElement.querySelector('.post-like-count');
                if (existingLikeElement) {
                    return;
                }
                
                const likeCount = post.agree?.agree_num || 0;
                const likeElement = document.createElement('span');
                likeElement.className = 'post-like-count';
                likeElement.innerHTML = `<span class="tail-info" style="color: #E74C3C;"><i style="font-size: 12px; margin-right: 3px;">❤</i>${likeCount}</span>`;
                
                const timeElement = tailElement.querySelector('.tail-info:last-of-type');
                if (timeElement) {
                    timeElement.after(likeElement);
                } else {
                    const postTailWrap = tailElement.querySelector('.post-tail-wrap');
                    if (postTailWrap) {
                        postTailWrap.appendChild(likeElement);
                    }
                }
                
                console.log("已显示楼层点赞数,回复ID:", post.id, "点赞数:", likeCount);
            }
        } else {
            console.warn("未找到对应的楼层元素,回复ID:", post.id);
        }
    }

    // 检查URL变化
    function checkUrlChange() {
        if (config.lastUrl !== location.href) {
            console.log("URL已变化,从", config.lastUrl, "变为", location.href);
            config.lastUrl = location.href;
            
            // 清除处理中状态
            config.isProcessing = false;
            
            // 获取当前帖子ID
            const tid = getTidFromUrl();
            if (tid) {
                console.log("URL变化后重新获取点赞数据,帖子ID:", tid);
                // 给页面一些时间加载
                setTimeout(() => {
                    // 获取帖子点赞数据
                    getPostData(tid);
                }, 1000);
            }
        }
    }

    // MD5算法实现
    function md5(string) {
        function RotateLeft(lValue, iShiftBits) {
            return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
        }
    
        function AddUnsigned(lX, lY) {
            var lX4, lY4, lX8, lY8, lResult;
            lX8 = (lX & 0x80000000);
            lY8 = (lY & 0x80000000);
            lX4 = (lX & 0x40000000);
            lY4 = (lY & 0x40000000);
            lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
            if (lX4 & lY4) {
                return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
            }
            if (lX4 | lY4) {
                if (lResult & 0x40000000) {
                    return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
                } else {
                    return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
                }
            } else {
                return (lResult ^ lX8 ^ lY8);
            }
        }
    
        function F(x, y, z) { return (x & y) | ((~x) & z); }
        function G(x, y, z) { return (x & z) | (y & (~z)); }
        function H(x, y, z) { return (x ^ y ^ z); }
        function I(x, y, z) { return (y ^ (x | (~z))); }
    
        function FF(a, b, c, d, x, s, ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        }
    
        function GG(a, b, c, d, x, s, ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        }
    
        function HH(a, b, c, d, x, s, ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        }
    
        function II(a, b, c, d, x, s, ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        }
    
        function ConvertToWordArray(string) {
            var lWordCount;
            var lMessageLength = string.length;
            var lNumberOfWords_temp1 = lMessageLength + 8;
            var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
            var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
            var lWordArray = Array(lNumberOfWords - 1);
            var lBytePosition = 0;
            var lByteCount = 0;
            while (lByteCount < lMessageLength) {
                lWordCount = (lByteCount - (lByteCount % 4)) / 4;
                lBytePosition = (lByteCount % 4) * 8;
                lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
                lByteCount++;
            }
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
            lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
            lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
            return lWordArray;
        }
    
        function WordToHex(lValue) {
            var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
            for (lCount = 0; lCount <= 3; lCount++) {
                lByte = (lValue >>> (lCount * 8)) & 255;
                WordToHexValue_temp = "0" + lByte.toString(16);
                WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
            }
            return WordToHexValue;
        }
    
        function Utf8Encode(string) {
            string = string.replace(/\r\n/g, "\n");
            var utftext = "";
    
            for (var n = 0; n < string.length; n++) {
                var c = string.charCodeAt(n);
    
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
            }
    
            return utftext;
        }
    
        var x = Array();
        var k, AA, BB, CC, DD, a, b, c, d;
        var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
        var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
        var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
        var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
    
        string = Utf8Encode(string);
    
        x = ConvertToWordArray(string);
    
        a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
    
        for (k = 0; k < x.length; k += 16) {
            AA = a; BB = b; CC = c; DD = d;
            a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
            d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
            c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
            b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
            a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
            d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
            c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
            b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
            a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
            d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
            c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
            b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
            a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
            d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
            c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
            b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
            a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
            d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
            c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
            b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
            a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
            d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
            c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
            b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
            a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
            d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
            c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
            b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
            a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
            d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
            c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
            b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
            a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
            d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
            c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
            b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
            a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
            d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
            c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
            b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
            a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
            d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
            c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
            b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
            a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
            d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
            c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
            b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
            a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
            d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
            c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
            b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
            a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
            d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
            c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
            b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
            a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
            d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
            c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
            b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
            a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
            d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
            c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
            b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
            a = AddUnsigned(a, AA);
            b = AddUnsigned(b, BB);
            c = AddUnsigned(c, CC);
            d = AddUnsigned(d, DD);
        }
    
        var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);
    
        return temp.toLowerCase();
    }

    // 添加样式
    function addCustomStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .thread-like-count, .post-like-count {
                display: inline-block;
                animation: fadeIn 0.5s;
            }
            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }
        `;
        document.head.appendChild(style);
    }

    // 注册(不可用)油猴菜单
    GM_registerMenuCommand("设置BDUSS", () => {
        const currentBDUSS = GM_getValue('BDUSS', '');
        const userBDUSS = prompt("请输入您的BDUSS值以获取点赞数据:", currentBDUSS || "");
        if (userBDUSS) {
            config.BDUSS = userBDUSS;
            GM_setValue('BDUSS', userBDUSS);
            
            // 刷新点赞数据
            const tid = getTidFromUrl();
            if (tid) getPostData(tid);
        }
    });

    // 初始化函数
    function runScript() {
        addCustomStyles();
        init();
        
        // 由于贴吧页面有时会动态加载内容,需要监听DOM变化
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                    // 如果有新的楼层添加,尝试显示点赞数据
                    for (let i = 0; i < mutation.addedNodes.length; i++) {
                        const node = mutation.addedNodes[i];
                        if (node.nodeType === 1 && node.classList.contains('l_post')) {
                            const tid = getTidFromUrl();
                            const currentPage = getCurrentPage();
                            const cacheKey = `${tid}_${currentPage}`;
                            
                            // 如果有缓存数据,尝试直接使用缓存的数据为新加载的楼层添加点赞数
                            if (config.postDataCache[cacheKey] && !config.isProcessing) {
                                const cachedData = config.postDataCache[cacheKey];
                                const postId = node.getAttribute('data-pid');
                                
                                if (postId && cachedData.post_list) {
                                    const post = cachedData.post_list.find(p => p && p.id === postId);
                                    if (post) {
                                        displayPostLikeCount(post);
                                    }
                                }
                            } else if (tid && !config.isProcessing) {
                                // 如果没有缓存数据或正在处理,则重新获取
                                getPostData(tid);
                            }
                            break;
                        }
                    }
                }
            });
        });
        
        observer.observe(document.body, { childList: true, subtree: true });
        
        // 监听URL变化(针对翻页)
        // 方法1:使用事件监听(适用于单页应用)
        window.addEventListener('popstate', () => {
            checkUrlChange();
        });
        
        // 方法2:周期性检查URL(适用于任何情况)
        setInterval(checkUrlChange, 1000);
        
        // 方法3:监听贴吧的翻页事件(适用于贴吧特定实现)
        document.addEventListener('click', (e) => {
            // 检查是否点击了翻页链接
            if (e.target && (e.target.classList.contains('pagination-item') || 
                             e.target.closest('.pagination-item'))) {
                // 给点击翻页一些时间来改变URL和加载内容
                setTimeout(checkUrlChange, 500);
            }
        });
    }

    // 确保脚本在页面加载后运行
    if (document.readyState === "loading") {
        window.addEventListener('DOMContentLoaded', runScript);
    } else {
        runScript();
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址