Greasy Fork

Greasy Fork is available in English.

Twitter/X (推特) t.co 直连 & 完整链接还原

将推特推文中的 t.co 链接替换为实际显示的完整 URL,同时修复被推特截断的站内链接显示(如 x.com/...)。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Twitter/X (推特) t.co 直连 & 完整链接还原
// @name:en      Twitter/X t.co Direct Link & Full URL
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  将推特推文中的 t.co 链接替换为实际显示的完整 URL,同时修复被推特截断的站内链接显示(如 x.com/...)。
// @description:en Bypasses the t.co redirection on Twitter/X, replacing the shortened links with the actual full URLs. It also restores truncated links (e.g., "x.com/long...") to their full original form.
// @author       You
// @match        https://twitter.com/*
// @match        https://x.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @license      MIT
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    function restoreLinks() {
        // 1. 扩大搜索范围:搜索所有可能是链接的元素
        // 排除掉已经处理过的 (data-resolved)
        const links = document.querySelectorAll('a:not([data-resolved])');

        links.forEach(link => {
            // --- 过滤阶段 ---

            // 如果链接里包含图片、视频、或者 div (通常是推特卡片/头像/导航栏),直接跳过
            if (link.querySelector('img, video, div')) return;

            // 获取隐藏的完整文本 (利用 textContent 获取 span 里的所有内容)
            let rawText = link.textContent.trim();

            // 核心判断 A:必须以省略号结尾 (说明被截断了)
            // Twitter 使用的是 unicode 省略号 '…'
            if (!rawText.endsWith('…')) {
                // 如果不是截断的链接,但它是 t.co 链接,我们依然需要处理它的跳转 (仅替换href)
                if (link.href.includes('t.co')) {
                     // 这种情况通常是短链接本身就是完整的,或者显示的文本不是 URL
                     // 我们只尝试移除 t.co 追踪,不做文本替换
                     bypassTcoOnly(link);
                }
                return; 
            }

            // 核心判断 B:看起来必须像一个 URL
            // 匹配 http 开头,或者 x.com / twitter.com 开头
            // 你的案例中 rawText 是 "https://x.com/imxiaohu/status/1996088137349751134?s=20…"
            const isUrlLike = /^(https?:\/\/|www\.|x\.com|twitter\.com)/.test(rawText);
            
            if (!isUrlLike) return;

            // --- 处理阶段 ---

            // 1. 清洗文本:移除末尾的省略号
            let cleanUrl = rawText.replace(/…$/, '');

            // 2. 补全协议 (如果只是 x.com 开头)
            if (!cleanUrl.startsWith('http')) {
                cleanUrl = 'https://' + cleanUrl;
            }

            // 标记为已处理
            link.dataset.resolved = "true";

            // 3. 替换显示文本 (解决你的核心需求)
            link.innerText = cleanUrl;
            
            // 4. 样式修正 (防止长链接破坏布局)
            link.style.wordBreak = "break-all";
            link.style.color = "rgb(29, 155, 240)"; // 保持推特蓝
            link.style.textDecoration = "underline";
            link.style.textDecorationColor = "rgba(29, 155, 240, 0.5)";

            // 5. 替换 href 为完整绝对路径
            link.href = cleanUrl;

            // 6. 阻止 Twitter 劫持 (对于站内链接,这会强制刷新页面访问,而不是 SPA 跳转,符合"直连"定义)
            link.addEventListener('click', (e) => {
                e.stopPropagation();
            }, true);
        });
    }

    // 辅助函数:仅处理 t.co 跳转,不修改文本 (用于那些没有被截断的短链接)
    function bypassTcoOnly(link) {
        if (link.dataset.resolved) return;
        
        let rawText = link.textContent.trim();
        // 如果显示文本本身就是 URL,就用显示文本作为 href
        if (/^(https?:\/\/|www\.)/.test(rawText) && !rawText.includes('…')) {
             link.href = rawText;
             link.dataset.resolved = "true";
             link.addEventListener('click', (e) => e.stopPropagation(), true);
        }
    }

    // 监听 DOM 变化
    const observer = new MutationObserver((mutations) => {
        restoreLinks();
    });

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

    // 初始运行
    restoreLinks();
})();