Twitter Fix URL

Adds a button to share a tweet with an alternative URL to the "X" link

// ==UserScript==
// @name         Twitter Fix URL
// @namespace    https://github.com/btcode23
// @version      0.3.2
// @description  Adds a button to share a tweet with an alternative URL to the "X" link
// @author       btcode23
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @match        https://twitter.com/*
// @match        https://x.com/*
// @icon         https://abs.twimg.com/responsive-web/client-web/icon-ios.77d25eba.png


// ==/UserScript==

const baseUrl = 'https://fixvx.com';

if (GM_getValue('ALT_URL') == undefined) {
    GM_setValue('ALT_URL', baseUrl);
}

const svg = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
    '<path d="M8 5.00005C7.01165 5.00082 6.49359 5.01338 6.09202 5.21799C5.71569 5.40973 5.40973 5.71569 5.21799 6.09202C5 6.51984 5 7.07989 5 8.2V17.8C5 18.9201 5 19.4802 5.21799 19.908C5.40973 20.2843 5.71569 20.5903 6.09202 20.782C6.51984 21 7.07989 21 8.2 21H15.8C16.9201 21 17.4802 21 17.908 20.782C18.2843 20.5903 18.5903 20.2843 18.782 19.908C19 19.4802 19 18.9201 19 17.8V8.2C19 7.07989 19 6.51984 18.782 6.09202C18.5903 5.71569 18.2843 5.40973 17.908 5.21799C17.5064 5.01338 16.9884 5.00082 16 5.00005M8 5.00005V7H16V5.00005M8 5.00005V4.70711C8 4.25435 8.17986 3.82014 8.5 3.5C8.82014 3.17986 9.25435 3 9.70711 3H14.2929C14.7456 3 15.1799 3.17986 15.5 3.5C15.8201 3.82014 16 4.25435 16 4.70711V5.00005M12 11V17M12 17L10 15M12 17L14 15" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>' +
    '</svg>';

function displayConfirmation() {
    const layers = document.getElementById('layers');

    let confirmation = document.getElementById('confirmCopyAltLink');
    if (!confirmation) {
        confirmation = document.createElement('div');
        confirmation.id = 'confirmCopyAltLink';
        confirmation.style.cssText = 'position: fixed; bottom: 10px; left: 0; right: 0; margin-left: auto; margin-right: auto; width: 100px; height: 40px;' +
            'background-color: rgba(29, 161, 242, 0.9); border-radius: 5px;'
        confirmation.innerHTML = '<p style="line-height: 40px; margin: 0; padding: auto; vertical-align: middle; text-align: center; font-weight: bold;">Copied</p>';
        layers.append(confirmation);
    }

    // show confirmation and then fade out
    confirmation.style.opacity = '1';
    confirmation.style.visibility = 'visible';
    confirmation.style.transition = '';
    setTimeout(function() {
        confirmation.style.opacity = '0';
        confirmation.style.visibility = 'hidden';
        confirmation.style.transition = '1s';
    }, 1000);
}

function copyAlternativeTwitterUrl(tweet) {
    // the part of the tweet with time seems to give the path to the tweet
    const tweetPath = tweet.querySelector('a:has(time)').getAttribute('href');

    // new share URL
    let newUrl = GM_getValue('ALT_URL') + tweetPath;

    navigator.clipboard.writeText(newUrl);
    displayConfirmation();
}

function designButton(tweet) {
    const group = tweet.querySelector('div[role="group"]');
    const otherIcon = group.querySelector('button[aria-label="Share post"]');

    const newIconPadding = document.createElement('div');
    group.insertBefore(newIconPadding, otherIcon.sibling);
    newIconPadding.classList.add('custom-copy-icon');
    const computedStyle = getComputedStyle(otherIcon);
    console.log(getComputedStyle(otherIcon));
    const adjustedIconSize = Number(computedStyle.height.split('px')[0]); // square size based on share icon's height
    newIconPadding.style.height = adjustedIconSize + 'px';
    newIconPadding.style.width = adjustedIconSize + 'px';
    const adjustedPadding = adjustedIconSize / 4; // move new share icon over relative to size
    newIconPadding.style.paddingLeft = adjustedPadding + 'px';

    const newIcon = document.createElement('div'); // create element to fit highlighted circle
    newIconPadding.append(newIcon);
    const computedStyleCircle = getComputedStyle(otherIcon.firstChild.firstChild.firstChild);
    const adjustedIconCircleSize = Number(computedStyleCircle.height.split('px')[0]);
    newIcon.style.height = adjustedIconSize + 'px';
    newIcon.style.width = adjustedIconSize + 'px';
    newIcon.style.padding = (adjustedIconSize - adjustedIconCircleSize) + 'px';
    newIcon.style.borderRadius = '50%'; // make background border a circle

    newIcon.innerHTML = svg; // add clipboard svg
    const icon = newIcon.querySelector('svg');
    const computedStyleIcon = getComputedStyle(otherIcon.querySelector('svg'));
    const iconOriginalColor = computedStyleIcon.color;
    icon.querySelector('path').style.stroke = iconOriginalColor; // set color to same as other icon
    //icon.style.padding = 50 / adjustedIconCircleSize + 'px';

    // highlight icon when mouseover event using twitter blue color
    // should look the same as the other icons
    newIcon.addEventListener('mouseover', function() {
        newIcon.style.backgroundColor = 'rgba(29, 161, 242, 0.1)';
        newIcon.querySelector('path').style.stroke = 'rgba(29, 161, 242, 1)';
    });

    // return to original style when mouseleave event
    newIcon.addEventListener('mouseleave', function() {
        newIcon.style.backgroundColor = '';
        newIcon.querySelector('path').style.stroke = iconOriginalColor;
    });

    // copy link using alternative domain
    newIcon.addEventListener('click', function(e) {
        e.stopPropagation();
        copyAlternativeTwitterUrl(tweet);
    });
}

function addNewShareButtons() {
    const tweets = document.querySelectorAll('article[data-testid="tweet"]');
    tweets.forEach(tweet => {
        if (!tweet.querySelector('.custom-copy-icon')) {
            designButton(tweet);
        }
    });
}

(function() {
    'use strict';

    // add new share button to each loaded tweet
    const observer = new MutationObserver(addNewShareButtons);
    observer.observe(
        document.body, {
            childList: true,
            subtree: true
        });
})();

// change the alternative URL for twitter
GM_registerMenuCommand('Setting', () => config());

function config() {
    let configPopupContainer = document.getElementById('ConfigTwitterFixUrlContainer')
    if (!configPopupContainer) {
        configPopupContainer = document.createElement('div');
    }
    configPopupContainer.id = 'ConfigTwitterFixUrlContainer';
    configPopupContainer.style.cssText = 'position: fixed; width: 100%; height: 100%; left: 0; top: 0; background: rgba(51,51,51,0.7);';
    document.body.appendChild(configPopupContainer);

    const configPopup = document.createElement('form');
    configPopup.innerHTML = '<p style="text-align: center;">Twitter Fix URL Config</p>';
    configPopup.style.cssText = 'position: fixed; top: 50%; left: 50%; padding: 10px; margin-top: -100px; margin-left: -150px; width: 325px; height: 200px; background-color: black; border: 2px solid white; border-radius: 25px; color: white; font-size: 24px;';

    const formFontSize = 'font-size: 18px;';
    const altUrlLabel = document.createElement('label');
    altUrlLabel.style.cssText = formFontSize;
    altUrlLabel.setAttribute('for', 'AltUrlTwttierFixUrl');
    altUrlLabel.innerHTML = 'Alternative URL:';

    const altUrlInput = document.createElement('input');
    altUrlInput.style.cssText = formFontSize + ' width: 280px;'
    altUrlInput.setAttribute('type', 'text');
    altUrlInput.setAttribute('id', 'AltUrlTwttierFixUrl');
    altUrlInput.setAttribute('name', 'AltUrlTwttierFixUrl');
    altUrlInput.setAttribute('value', GM_getValue('ALT_URL'));

    configPopup.append(altUrlLabel);
    configPopup.append(document.createElement('br'));
    configPopup.append(altUrlInput);

    const buttonCSS = formFontSize + ' width: 75px; height: 30px; border: 2px solid white; text-align: center; padding: 0px; border-radius: 10px; margin-left: 12.25px; margin-right: 12.25px;';

    const altUrlResetButton = document.createElement('button');
    altUrlResetButton.setAttribute('type', 'button');
    altUrlResetButton.setAttribute('value', 'reset');
    altUrlResetButton.style.cssText = buttonCSS;
    altUrlResetButton.innerHTML = 'Reset';
    altUrlResetButton.addEventListener('click', function(e) {
        altUrlInput.value = baseUrl;
    });

    const altUrlCancelButton = document.createElement('button');
    altUrlCancelButton.setAttribute('type', 'button');
    altUrlCancelButton.setAttribute('value', 'cancel');
    altUrlCancelButton.style.cssText = buttonCSS;
    altUrlCancelButton.innerHTML = 'Cancel';
    altUrlCancelButton.addEventListener('click', function(e) {
        configPopupContainer.style.display = 'none';
    });

    const altUrlSubmitButton = document.createElement('button');
    altUrlSubmitButton.setAttribute('type', 'button');
    altUrlSubmitButton.setAttribute('value', 'submit');
    altUrlSubmitButton.style.cssText = buttonCSS;
    altUrlSubmitButton.innerHTML = 'Submit';
    altUrlSubmitButton.addEventListener('click', function(e) {
        GM_setValue('ALT_URL', altUrlInput.value);
        configPopupContainer.style.display = 'none';
    });

    configPopup.append(document.createElement('br'));
    configPopup.append(document.createElement('br'));
    configPopup.appendChild(altUrlResetButton);
    configPopup.appendChild(altUrlCancelButton);
    configPopup.appendChild(altUrlSubmitButton);
    configPopupContainer.appendChild(configPopup);

    document.addEventListener('mouseup', function(e) {
        if (e.target.id == 'ConfigTwitterFixUrlContainer') {
            configPopupContainer.style.display = 'none';
        }
    });
}

QingJ © 2025

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