您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows you to find posts with a lot of reactions (likes) in Telegram Web A.
// ==UserScript== // @name Tg Sort by reactions // @name:ru Tg Сортировка по реакциям // @name:zh Tg 按反应排序 // @version 0.1.5 // @description Allows you to find posts with a lot of reactions (likes) in Telegram Web A. // @description:ru Позволяет найти сообщения с наибольшим количеством реакций (лайков) в Телеграм Web A. // @description:zh 允许您在电报 Web A 中找到有很多反应(喜欢)的消息。 // @author sokollondon // @match https://web.telegram.org/a/* // @icon https://www.google.com/s2/favicons?sz=64&domain=telegram.org // @require http://code.jquery.com/jquery-3.3.1.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jquery-scrollTo/2.1.3/jquery.scrollTo.min.js // @grant none // @license MIT // <-- Лицензия добавлена здесь! // @namespace https://gist.github.com/sokollondon/4be0d13f33a371895308ed7b1dc15fcf // ==/UserScript== (function() { 'use strict'; // Ensure jQuery is loaded and available if (typeof jQuery === 'undefined') { console.error('Tg Sort by Reactions: jQuery is not loaded.'); return; } const $ = jQuery.noConflict(true); // Use noConflict to avoid conflicts console.log('Tg Sort by Reactions: Script initialized.'); // --- Styles for the Sort Button --- const style = ` #tg-sort-reactions-btn { position: fixed; top: 73px; right: 16px; width: 45px; height: 45px; padding: 5px; border-radius: 10px; text-align: center; font-size: 20px; padding-top: 5px; z-index: 2000; /* Increased z-index to ensure visibility */ opacity: 0.7; background: url(https://cdn0.iconfinder.com/data/icons/font-awesome-solid-vol-4/512/sort-amount-down-alt-64.png) no-repeat center center; background-size: 32px; background-color: var(--tg-theme-background-color, #fff); /* Use Telegram theme variable */ border: 1px solid var(--tg-theme-border-color, #e0e0e0); /* Add border */ box-shadow: 0 2px 5px rgba(0,0,0,0.2); /* Add subtle shadow */ cursor: pointer; transition: opacity 0.2s ease-in-out, background-color 0.2s ease-in-out; } #tg-sort-reactions-btn:hover { opacity: 1; } .theme-dark #tg-sort-reactions-btn { background-color: var(--tg-theme-dark-background-color, #a9a9a9); /* Dark theme adjustments */ border-color: var(--tg-theme-dark-border-color, #606060); } @media screen and (max-width: 600px) { #tg-sort-reactions-btn { top: 111px; right: 8px; } } `; $('head').append('<style id="tg-sort-reactions-style">' + style + '</style>'); // --- Create and Append the Sort Button --- const sortButtonHtml = "<div id='tg-sort-reactions-btn' title='Sort by reactions count'></div>"; $('body').prepend(sortButtonHtml); console.log('Tg Sort by Reactions: Sort button added to DOM.'); // --- Helper Function to Get Reaction Count --- function getReactionCount($messageElement) { // In Telegram Web A, reactions are typically in a div with role="button" inside a reaction container. // The count is often within a span or element with specific class. // Look for elements that represent reactions. Common classes might include: // .Reactions is from older webk. Now it's something like .reaction-button or a span. // The reaction counts are usually numeric text within a span. let totalReactions = 0; // Find reaction containers within the message bubble. // This selector targets the element that usually wraps all reaction buttons. // It's usually a div with a role="button" and aria-label containing the count. const $reactionContainer = $messageElement.find('[data-list="reactions-list"]'); if ($reactionContainer.length) { // Find individual reaction buttons/spans within the container // The structure is often: <div class="reactions-box"> <button> 👍<span>23</span> </button> </div> // Or directly within elements with text content. $reactionContainer.find('span[class*="text"], div[aria-label$="reactions"]').each(function() { let text = $(this).text().trim(); let count = parseReactionNumber(text); totalReactions += count; }); // Fallback: If no specific spans are found, check the aria-label of the main container itself // sometimes the total count is on the parent if (totalReactions === 0 && $reactionContainer.attr('aria-label')) { const ariaLabel = $reactionContainer.attr('aria-label'); const match = ariaLabel.match(/(\d+\.?\d*)([KM]?)\sreactions?/i); if (match) { totalReactions = parseReactionNumber(match[1] + (match[2] || '')); } } } else { // Fallback for older structures or if the above fails // Try to find elements that look like reaction counts directly // This might catch cases where reaction counts are simpler spans $messageElement.find('span[class*="reaction"], div[class*="reaction"]').each(function() { let text = $(this).text().trim(); let count = parseReactionNumber(text); totalReactions += count; }); } return totalReactions; } // --- Function to Parse K/M numbers --- function parseReactionNumber(str) { let value = parseFloat(str.replace(/[^0-9.,]/g, '').replace(/,/g, '.')); if (isNaN(value)) return 0; if (str.toUpperCase().includes('K')) { value *= 1000; } else if (str.toUpperCase().includes('M')) { value *= 1000000; } return value; } // --- Click Handler for the Sort Button --- $('#tg-sort-reactions-btn').on('click', function() { console.log('Tg Sort by Reactions: Sort button clicked.'); const $chatMessagesContainer = $('.Transition.active .MessageList.open'); // Specific to Web A chat view if (!$chatMessagesContainer.length) { console.warn('Tg Sort by Reactions: Could not find the active chat messages container.'); alert('Please open a chat with messages to sort.'); return; } const $messageList = $chatMessagesContainer.find('.messages-container'); // This is where messages are directly appended if (!$messageList.length) { console.warn('Tg Sort by Reactions: Could not find the message list container.'); alert('Could not find messages in the current chat.'); return; } // Get all message bubbles // Web A message elements usually have a class like 'Message' or similar, and data attributes for ID. // Example: <div class="message-wrapper"> or <div class="Message"> // Look for common parent of messages. const $messageBubbles = $chatMessagesContainer.find('[data-message-id]'); // All elements with a message ID if ($messageBubbles.length === 0) { console.warn('Tg Sort by Reactions: No messages found in the current view.'); alert('No messages with reactions found to sort.'); return; } console.log(`Tg Sort by Reactions: Found ${$messageBubbles.length} messages. Sorting...`); // Detach, sort, and re-append $messageBubbles.detach().sort(function(a, b) { const $a = $(a); const $b = $(b); let aReactions = getReactionCount($a); let bReactions = getReactionCount($b); // console.log(`Message A (${$a.attr('data-message-id')}): ${aReactions} reactions`); // console.log(`Message B (${$b.attr('data-message-id')}): ${bReactions} reactions`); return bReactions - aReactions; // Sort in descending order (highest reactions first) }).appendTo($messageList); console.log('Tg Sort by Reactions: Messages sorted.'); // Scroll to the top or to the message with most reactions if it's not visible // It's more logical to scroll to the top after sorting by highest reactions. $chatMessagesContainer.scrollTop(0); // Scroll to the very top alert('Messages sorted by reactions (highest first)!'); }); // --- Mutation Observer to Handle Dynamic Content --- // Telegram Web A loads content dynamically, so we need to observe for chat changes. const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { // Check if the added nodes contain chat containers, indicating a chat change const chatContainerAdded = Array.from(mutation.addedNodes).some(node => node.nodeType === 1 && $(node).find('.MessageList.open').length ); if (chatContainerAdded) { console.log('Tg Sort by Reactions: Detected new chat container. Ready for sorting.'); // No need to re-add button, it's fixed. Just log readiness. } } }); }); // Observe the body for changes in chat content observer.observe(document.body, { childList: true, subtree: true }); console.log('Tg Sort by Reactions: MutationObserver started.'); })();