Greasy Fork

Greasy Fork is available in English.

Telegram Web Sort by Reactions (Auto-Clear & Jump)

Find messages with the most reactions. Compact view, precise jump, and auto-clear on chat switch.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Telegram Web Sort by Reactions (Auto-Clear & Jump)
// @name:zh-CN   Telegram Web 按回应排序 (自动清空 & 精准跳转)
// @namespace    https://gist.github.com/panwanke/c30874ba5a0996be2fff5bcf609efd6f
// @version      1.0.1
// @description  Find messages with the most reactions. Compact view, precise jump, and auto-clear on chat switch.
// @description:zh-CN 帮助您查找 Telegram 中回应最多的消息。支持简约面板预览、精准跳转以及切换频道自动清空。
// @author       epool
// @license      MIT
// @match        https://web.telegram.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=telegram.org
// @require      http://code.jquery.com/jquery-3.3.1.min.js
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ==========================================
    // 1. 注入 UI 样式 (极致去图纯文本模式 + 新按钮)
    // ==========================================
    let style = `
        #sBtn {
            position: fixed; top: 73px; right: 16px; width: 45px; height: 45px;
            padding: 5px; border-radius: 10px; z-index: 9999; opacity: 0.8;
            background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAEgAAAABAAAASAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgBAABAAAAQAAAAAOgBAABAAAAQAAAAAAAAABuEYT5AAABkklEQVR4nO2bPU7EMBBGX74OcQIKjgFnYMVPh1YcZ7kMFQeh4g4sghtQYhSJaoWCHduJZ8dPcud8sZ3VTPZJgU6n05nmGngHwsHYA5uCeaXyi7OfWORb4bwS+UkMEXNCgYyUvNz8JIRzhHOEc7TCPccOkFIwVyf8M1LZRHaCsQNccYRdoCmEc4RzhHOEc4RzhHOEc7TCPWN9QDO+IBR+E0zxAdV9wWDAB1DzTVQ4RzhHOEeN+4AmfEFYyQcs4guGiDndBxwzwjnCOcI5wjnCOcI5MuwDFvMFoWEfkO0LhsgDyM1IySvN5PqEc4RzhHNk3Acs4gtCoz6gmC+4Az4qLGDMvMUAHxWfwpI/99k1YKiYb8IW3QPfFZ7+mLnFCLsKB/CIIQbgqeDmny2+Y5wALwU2/wqcYpSzzB79CZxjnAvga8bmx2suG/IBWb5gm9gZxrkPDfqALF+wq1TxQ2MjuzOkVnwzBxDTGeZUfFMHMNUZ5lZ8cwfwV2eIrfimi+AhN78X7DP/5i7lA5r4HqHT6dA0P32R2X1NXS7nAAAAAElFTkSuQmCC") no-repeat center center;
            background-size: 32px; background-color: #fff; cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.2s;
        }
        #sBtn:hover { opacity: 1; transform: scale(1.05); }
        .theme-dark #sBtn { background-color: #2c2c2c; }

        #tg-sort-panel {
            position: fixed; top: 0; right: -420px; width: 350px; height: 100vh;
            background: #ffffff; z-index: 10000; box-shadow: -5px 0 15px rgba(0,0,0,0.1);
            transition: right 0.3s ease; display: flex; flex-direction: column;
        }
        #tg-sort-panel.open { right: 0; }
        .theme-dark #tg-sort-panel { background: #212121; color: #fff; border-left: 1px solid #333; }

        .tg-panel-header {
            padding: 15px 20px; background: #3390ec; color: white; font-weight: bold;
            display: flex; justify-content: space-between; align-items: center; font-size: 16px;
        }
        .tg-panel-actions span { cursor: pointer; font-size: 18px; margin-left: 12px; opacity: 0.8; transition: opacity 0.2s; }
        .tg-panel-actions span:hover { opacity: 1; }

        .tg-panel-content { flex: 1; overflow-y: auto; padding: 15px 10px; background: #f4f4f5; }
        .theme-dark .tg-panel-content { background: #0f0f0f; }

        .tg-sorted-msg-wrap {
            margin-bottom: 12px; background: #fff; border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 10px; position: relative;
            cursor: pointer; transition: background 0.2s ease; border: 1px solid transparent;
            overflow: hidden;
        }
        .theme-dark .tg-sorted-msg-wrap { background: #181818; }
        .tg-sorted-msg-wrap:hover { background: #f0f8ff; border-color: #3390ec; }
        .theme-dark .tg-sorted-msg-wrap:hover { background: #2c3e50; }

        .tg-rank-badge {
            display: inline-block; background: #ff4500; color: #fff;
            font-size: 12px; font-weight: bold; padding: 2px 6px;
            border-radius: 6px; margin-bottom: 4px;
        }

        /* 暴力隐藏多媒体及干扰项 */
        .tg-sorted-msg-wrap .attachment,
        .tg-sorted-msg-wrap .album-item,
        .tg-sorted-msg-wrap .media-container,
        .tg-sorted-msg-wrap img:not(.media-sticker),
        .tg-sorted-msg-wrap video,
        .tg-sorted-msg-wrap .media-wrap,
        .tg-sorted-msg-wrap .message-media,
        .tg-sorted-msg-wrap .ReplyInfo,
        .tg-sorted-msg-wrap .stacked-avatars,
        .tg-sorted-msg-wrap .bubble-beside-button,
        .tg-sorted-msg-wrap .bubble-tail {
            display: none !important;
        }

        /* 文字折叠 */
        .tg-sorted-msg-wrap .message,
        .tg-sorted-msg-wrap .text-content,
        .tg-sorted-msg-wrap .MessageText,
        .tg-sorted-msg-wrap .translatable-message {
            display: -webkit-box !important;
            -webkit-line-clamp: 2 !important;
            -webkit-box-orient: vertical !important;
            overflow: hidden !important;
            font-size: 13px !important;
            color: #555;
            line-height: 1.4;
            white-space: normal !important;
            margin-top: 2px;
        }
        .theme-dark .tg-sorted-msg-wrap .message,
        .theme-dark .tg-sorted-msg-wrap .text-content,
        .theme-dark .tg-sorted-msg-wrap .MessageText,
        .theme-dark .tg-sorted-msg-wrap .translatable-message { color: #aaa; }

        /* 反应按钮缩放 */
        .tg-sorted-msg-wrap reaction-element,
        .tg-sorted-msg-wrap .reaction {
            transform: scale(0.85);
            transform-origin: left top;
            margin-top: 5px;
        }

        /* 闪烁高亮动画 */
        @keyframes tg-flash {
            0% { background-color: rgba(51, 144, 236, 0.4); }
            100% { background-color: transparent; }
        }
        .tg-highlight-msg { animation: tg-flash 2s ease-out; }

    `;
    $('body').prepend('<style>' + style + '</style>');

    // ==========================================
    // 2. 构建 UI DOM (新增清空按钮)
    // ==========================================
    $('body').append("<div id='sBtn' title='Scan and Sort Reactions'></div>");
    $('body').append(`
        <div id="tg-sort-panel">
            <div class="tg-panel-header">
                <span>🏆 Top Reactions</span>
                <div class="tg-panel-actions">
                    <span class="tg-panel-clear" title="清空列表">🗑️</span>
                    <span class="tg-panel-close" title="关闭面板">✖</span>
                </div>
            </div>
            <div class="tg-panel-content">
                <div style="padding:20px; text-align:center; color:#888;">暂无数据。请点击左侧按钮进行扫描。</div>
            </div>
        </div>
    `);

    // 绑定关闭按钮
    $('.tg-panel-close').click(() => $('#tg-sort-panel').removeClass('open'));

    // 绑定手动清空按钮
    $('.tg-panel-clear').click(function() {
        $('.tg-panel-content').html('<div style="padding:20px; text-align:center; color:#888;">已清空。请点击左侧按钮重新扫描当前频道。</div>');
    });

    // ==========================================
    // 3. 智能检测频道切换 (Auto-clear on chat switch)
    // ==========================================
    let currentChatUrl = window.location.href;
    setInterval(() => {
        if (window.location.href !== currentChatUrl) {
            currentChatUrl = window.location.href;
            // 当网址发生变化(切换了频道/群组),自动收起面板并清空数据
            if ($('#tg-sort-panel').hasClass('open')) {
                $('#tg-sort-panel').removeClass('open');
            }
            $('.tg-panel-content').html('<div style="padding:20px; text-align:center; color:#888;">检测到频道切换,数据已自动重置。请重新扫描。</div>');
        }
    }, 1000); // 每秒轻量级检测一次网址

    // ==========================================
    // 4. 数字转换函数
    // ==========================================
    $.fn.text2qty = function () {
        let counterSpan = $(this).find('.reaction-counter');
        let textToParse = counterSpan.length > 0 ? counterSpan.text() : $(this).text();
        if (!textToParse) return 0;

        let qty = parseFloat(textToParse.replace(/[^0-9.,]/g, '').replace(/[,]/g, '.')) || 0;
        if (textToParse.match(/K$/i)) qty *= 1000;
        if (textToParse.match(/M$/i)) qty *= 1000000;
        if (qty > 0) return qty;

        return 0;
    };

    // ==========================================
    // 5. 扫描、提取与跳转逻辑
    // ==========================================
    $('#sBtn').click(function() {
        let msgArray = [];

        let messages = $('.bubble, .message-list-item, .Message');

        messages.each(function() {
            let item = $(this);
            let reactSum = 0;

            if (item.hasClass('bubble') && item.find('.bubble-content').length === 0) return;

            let possibleId1 = item.attr('id');
            let possibleId2 = item.attr('data-message-id');
            let possibleId3 = item.attr('data-mid');

            item.find('reaction-element, .reaction, .Reactions .Button').each(function() {
                reactSum += $(this).text2qty();
            });

            if (reactSum > 0) {
                let cloneNode = item.clone().removeAttr('id');
                cloneNode.find('reaction-element, .reaction, .Reactions .Button, .replies').css('pointer-events', 'none');

                msgArray.push({
                    score: reactSum,
                    id1: possibleId1,
                    id2: possibleId2,
                    id3: possibleId3,
                    dom: cloneNode
                });
            }
        });

        // 降序排序
        msgArray.sort((a, b) => b.score - a.score);

        let contentContainer = $('.tg-panel-content');
        contentContainer.empty();

        if (msgArray.length === 0) {
            contentContainer.append('<div style="padding:20px; text-align:center; color:#888;">当前可视区域未扫描到包含互动的消息。<br><br>💡 提示:请先在聊天窗口往上滚动一段距离加载历史记录,然后再点击扫描。</div>');
        } else {
            msgArray.forEach((msgObj, index) => {
                let wrapper = $('<div class="tg-sorted-msg-wrap"></div>');
                let badge = $(`<div class="tg-rank-badge">#${index + 1} (♥ ${msgObj.score})</div>`);

                wrapper.append(badge);
                wrapper.append(msgObj.dom);

                wrapper.click(function() {
                    let targetElement = null;

                    if (msgObj.id3) targetElement = $(`[data-mid="${msgObj.id3}"]`);
                    if (!targetElement || !targetElement.length) {
                        if (msgObj.id1) targetElement = $('#' + $.escapeSelector(msgObj.id1));
                    }
                    if (!targetElement || !targetElement.length) {
                        if (msgObj.id2) targetElement = $(`[data-message-id="${msgObj.id2}"]`);
                    }

                    if (targetElement && targetElement.length > 0) {
                        try {
                            targetElement.get(0).scrollIntoView({ behavior: 'smooth', block: 'center' });

                            targetElement.removeClass('tg-highlight-msg');
                            setTimeout(() => {
                                targetElement.addClass('tg-highlight-msg');
                            }, 10);

                        } catch(e) {
                            console.error("Jump failed:", e);
                        }
                    } else {
                        alert('⚠️ 消息已被内存清理,请在聊天窗口向上滚动一段距离后重试。');
                    }
                });

                contentContainer.append(wrapper);
            });
        }

        $('#tg-sort-panel').addClass('open');
    });

})();