您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extract tweet ID from clipboard or current page and uses search algorithm to sort tweets (both quotes and replies)
// ==UserScript== // @name Twitter Search Tweet Conversation // @namespace http://tampermonkey.net/ // @version 1.7 // @description Extract tweet ID from clipboard or current page and uses search algorithm to sort tweets (both quotes and replies) // @author colleidoscope // @match https://x.com/* // @match https://www.x.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; let uiContainer = null; let debounceTimeout = null; function extractTweetID(url) { const regex = /^https?:\/\/x\.com\/[^\/]+\/status\/(\d+)/; const match = url.match(regex); return match ? match[1] : null; } function buildSearchQuery(tweetID, option) { let query = `filter:has_engagement (-filter:safe OR filter:safe)`; switch (option) { case 'min_faves_50': query = `min_faves:50 ${query} (quoted_tweet_id:${tweetID} OR conversation_id:${tweetID})`; break; case 'min_faves_5': query = `min_faves:5 ${query} (quoted_tweet_id:${tweetID} OR conversation_id:${tweetID})`; break; case 'no_min_faves': query = `${query} (quoted_tweet_id:${tweetID} OR conversation_id:${tweetID})`; break; case 'no_min_faves_no_conversation': query = `${query} quoted_tweet_id:${tweetID}`; break; } return query; } async function handleSearch(option, source) { try { let url; if (source === 'clipboard') { const text = await navigator.clipboard.readText(); url = text.trim(); } else if (source === 'current') { url = window.location.href; } const tweetID = extractTweetID(url); if (tweetID) { const query = buildSearchQuery(tweetID, option); const encodedQuery = encodeURIComponent(query); const searchURL = `https://x.com/search?q=${encodedQuery}`; window.open(searchURL, '_blank'); } else { alert("The selected source does not contain a valid X post URL."); } } catch (err) { console.error('Error accessing source:', err); alert("Failed to read from the selected source. Please ensure permissions are granted."); } } function removeUI() { if (uiContainer && document.body.contains(uiContainer)) { document.body.removeChild(uiContainer); uiContainer = null; } } function addUI() { if (uiContainer) return; // Don't add if already exists // Create the container uiContainer = document.createElement('div'); uiContainer.style.position = 'fixed'; uiContainer.style.bottom = '60px'; uiContainer.style.right = '80px' uiContainer.style.zIndex = '1000'; uiContainer.style.display = 'flex'; uiContainer.style.alignItems = 'center'; uiContainer.style.backgroundColor = '#2f3336'; uiContainer.style.padding = '10px 15px'; uiContainer.style.borderRadius = '30px'; uiContainer.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.3)'; uiContainer.style.transition = 'all 0.3s ease-in-out'; // Create the dropdown for search options const dropdown = document.createElement('select'); dropdown.style.marginRight = '15px'; dropdown.style.padding = '8px 12px'; dropdown.style.borderRadius = '20px'; dropdown.style.border = 'none'; dropdown.style.backgroundColor = '#1c1c1e'; dropdown.style.color = '#fff'; dropdown.style.fontSize = '14px'; dropdown.style.fontWeight = '500'; dropdown.style.cursor = 'pointer'; dropdown.style.outline = 'none'; // Add options to the dropdown const options = [ { value: 'min_faves_50', text: 'Min Faves: 50' }, { value: 'min_faves_5', text: 'Min Faves: 5' }, { value: 'no_min_faves', text: 'No Min Faves' }, { value: 'no_min_faves_no_conversation', text: 'Just Quotes' } ]; options.forEach(opt => { const optionElement = document.createElement('option'); optionElement.value = opt.value; optionElement.innerText = opt.text; dropdown.appendChild(optionElement); }); // Create the toggle switch for source selection const toggleContainer = document.createElement('div'); toggleContainer.style.display = 'flex'; toggleContainer.style.alignItems = 'center'; toggleContainer.style.marginRight = '15px'; const toggleLabel = document.createElement('span'); toggleLabel.innerText = 'Clipboard:'; toggleLabel.style.color = '#fff'; toggleLabel.style.marginRight = '8px'; toggleLabel.style.fontSize = '14px'; toggleLabel.style.fontWeight = '500'; const toggleSwitch = document.createElement('label'); toggleSwitch.style.position = 'relative'; toggleSwitch.style.display = 'inline-block'; toggleSwitch.style.width = '50px'; toggleSwitch.style.height = '24px'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.style.opacity = '0'; checkbox.style.width = '0'; checkbox.style.height = '0'; const slider = document.createElement('span'); slider.style.position = 'absolute'; slider.style.cursor = 'pointer'; slider.style.top = '0'; slider.style.left = '0'; slider.style.right = '0'; slider.style.bottom = '0'; slider.style.backgroundColor = '#ccc'; slider.style.transition = '.4s'; slider.style.borderRadius = '24px'; // Create the slider's circle const sliderBefore = document.createElement('span'); sliderBefore.style.position = 'absolute'; sliderBefore.style.height = '18px'; sliderBefore.style.width = '18px'; sliderBefore.style.left = '3px'; sliderBefore.style.bottom = '3px'; sliderBefore.style.backgroundColor = 'white'; sliderBefore.style.transition = '.4s'; sliderBefore.style.borderRadius = '50%'; slider.appendChild(sliderBefore); toggleSwitch.appendChild(checkbox); toggleSwitch.appendChild(slider); // Initialize toggle state from localStorage const savedSource = localStorage.getItem('searchSource') || 'clipboard'; if (savedSource === 'clipboard') { checkbox.checked = true; } // Update slider color based on state function updateSlider() { if (checkbox.checked) { slider.style.backgroundColor = '#4caf50'; sliderBefore.style.transform = 'translateX(26px)'; } else { slider.style.backgroundColor = '#ccc'; sliderBefore.style.transform = 'translateX(0)'; } } updateSlider(); // Add event listener to toggle switch checkbox.addEventListener('change', () => { const source = checkbox.checked ? 'clipboard' : 'current'; localStorage.setItem('searchSource', source); updateSlider(); }); toggleContainer.appendChild(toggleLabel); toggleContainer.appendChild(toggleSwitch); // Create the search button const button = document.createElement('button'); button.innerHTML = '🔍'; button.style.padding = '8px 16px'; button.style.border = 'none'; button.style.borderRadius = '20px'; button.style.backgroundColor = '#1da1f2'; button.style.color = '#fff'; button.style.fontSize = '14px'; button.style.fontWeight = '600'; button.style.cursor = 'pointer'; button.style.transition = 'background-color 0.3s ease, transform 0.2s ease'; button.onmouseover = () => { button.style.backgroundColor = '#0d8ddb'; button.style.transform = 'scale(1.05)'; }; button.onmouseout = () => { button.style.backgroundColor = '#1da1f2'; button.style.transform = 'scale(1)'; }; button.addEventListener('click', () => { const selectedOption = dropdown.value; const source = checkbox.checked ? 'clipboard' : 'current'; handleSearch(selectedOption, source); }); // Append all elements to the container uiContainer.appendChild(dropdown); uiContainer.appendChild(toggleContainer); uiContainer.appendChild(button); // Append the container to the body if (!document.body.contains(uiContainer)) { document.body.appendChild(uiContainer); } } function debounce(func, wait) { return function executedFunction(...args) { const later = () => { clearTimeout(debounceTimeout); func(...args); }; clearTimeout(debounceTimeout); debounceTimeout = setTimeout(later, wait); }; } const debouncedInitialize = debounce(() => { if (document.body && !uiContainer) { addUI(); } }, 1000); // Initialize on page load if (document.readyState === 'loading') { window.addEventListener('load', debouncedInitialize); } else { debouncedInitialize(); } // Monitor URL changes let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; debouncedInitialize(); } }).observe(document, {subtree: true, childList: true}); // Clean up when navigating away window.addEventListener('beforeunload', () => { removeUI(); clearTimeout(debounceTimeout); }); })();