您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds buttons at the top of tweets to easily copy Twitter / X proxies
// ==UserScript== // @name Copy fxtwitter or fixupx link buttons // @name:es Botones de copiar enlace de fxtwitter o fixupx // @namespace http://tampermonkey.net/ // @version 1.1.0 // @description Adds buttons at the top of tweets to easily copy Twitter / X proxies // @description:es Añade botones en la parte superior de tweets para copiar proxies de Twitter / X fácilmente // @author LuisAlfredo92 // @match *x.com/* // @match *twitter.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=x.com // @grant none // @license GPL3 // @supportURL https://github.com/LuisAlfredo92/copy-fxtwitter-or-fixupx-link-buttons // ==/UserScript== (function () { 'use strict'; // Variables const ProxyType = { fxtwitter: "fxtwitter", fixupx: "fixupx", vxtwitter: "vxtwitter", girlcockx: "girlcockx", stupidpenisx: "stupidpenisx" }; const languages = { en: "English", es: "Español" }; const re = new RegExp(".*(x.com|twitter.com)\/.+\/status\/.+"); let exists = false; // Labels const englishLabels = { copyButton: "Copy {0} link", copied: "Copied!", language: "Language", enabledButtons: "Enabled buttons", save: "Save" }; const spanishLabels = { copyButton: "Copiar enlace de {0}", copied: "¡Copiado!", language: "Idioma", enabledButtons: "Botones habilitados", save: "Guardar" }; // Get data from local storage let savedData; let savedDataString = localStorage.getItem("fxtwitterFixupxButtons"); if (!savedDataString) { savedData = { fxtwitter: true, fixupx: true, vxtwitter: false, girlcockx: false, stupidpenisx: false, language: "en" }; localStorage.setItem("fxtwitterFixupxButtons", JSON.stringify(savedData)); } else { savedData = JSON.parse(savedDataString); } // There must be a better way to do this let labels; switch (savedData.language) { case "es": labels = spanishLabels; break; default: labels = englishLabels; break; } // Function to change url const modifyAndSaveUrl = async (type, button) => { // Get URL const currentUrl = window.location.href; const isTwitter = currentUrl.includes("twitter.com"); let modifiedUrl, proxyHost; switch (type) { case ProxyType.fxtwitter: proxyHost = "fxtwitter.com"; break; case ProxyType.girlcockx: proxyHost = "girlcockx.com"; break; case ProxyType.stupidpenisx: proxyHost = "stupidpenisx.com"; break; default: proxyHost = "fixupx.com"; break; } modifiedUrl = currentUrl.replace(isTwitter ? "twitter.com" : "x.com", proxyHost); // Copy to url try { await navigator.clipboard.writeText(modifiedUrl); console.log("Modified URL copied to clipboard: ", modifiedUrl); // Change the button text to "Copied!", then, wait 3 seconds and change it back to the original text const originalText = button.innerText; button.innerText = labels.copied; setTimeout(() => button.innerText = originalText, 3000); } catch (ex) { console.error("Failed to copy the modified URL: ", ex); } } // Function to create buttons const createButton = (type) => { const button = document.createElement("BUTTON"); button.appendChild(document.createTextNode(labels.copyButton.replace("{0}", type))); button.addEventListener("click", () => modifyAndSaveUrl(type, button)); button.style.width = 80 / totalButtons + "%"; button.style.flexGrow = "1"; button.style.flexShrink = "0"; button.style.textAlign = "center"; button.style.fontFamily = "TwitterChirp"; button.style.border = "none"; button.style.borderRadius = "25px"; button.style.padding = "10px 0px"; button.style.fontWeight = "bold"; return button; } // Function to create settings const createSettings = () => { const settings = document.createElement("DIV"); settings.style.width = "250px"; settings.style.fontFamily = "TwitterChirp"; settings.style.display = "none"; settings.style.flexDirection = "column"; settings.style.position = "absolute"; settings.style.top = "10px"; settings.style.right = "15%"; settings.style.backgroundColor = "Black"; settings.style.color = "White"; settings.style.padding = "10px"; settings.style.zIndex = "99"; settings.style.border = "1px solid White"; // Create checkboxes let checkboxesHtml = ""; for (const key in ProxyType) { checkboxesHtml += ` <div style="display:flex;flex-direction:row;gap:10px;"> <input type="checkbox" id="${key}" name="${key}" ${savedData[key] ? "checked" : ""}> <label for="${key}">${ProxyType[key]}</label> </div> `; } // Create options for select let optionsHtml = ""; for (const key in languages) { optionsHtml += `<option value="${key}" ${key === savedData.language ? "selected=\"selected\"" : ""}>${languages[key]}</option>`; } settings.innerHTML = ` <div style="display:flex;flex-direction:row;gap:10px;"> <label for="lang">${labels.language}: </label> <select name="lang" id="lang" value="${savedData.language}"> ${optionsHtml} </select> <button style="margin-left:auto;">X</button> </div> <p>${labels.enabledButtons}:</p> ${checkboxesHtml} <button style="margin-top:10px;">${labels.save}</button> `; // Add event listeners const buttons = settings.querySelectorAll("button"); const closeButton = buttons[0], saveButton = buttons[1]; closeButton.addEventListener("click", () => settings.style.display = "none"); // Save settings saveButton.addEventListener("click", () => { const checkboxes = settings.querySelectorAll("input[type=checkbox]"); checkboxes.forEach(checkbox => { savedData[checkbox.name] = checkbox.checked; }); savedData.language = settings.querySelector("#lang").value; localStorage.setItem("fxtwitterFixupxButtons", JSON.stringify(savedData)); // Update labels switch (savedData.language) { case "es": labels = spanishLabels; break; default: labels = englishLabels; break; } // Update buttons saveButton.innerText = labels.save; }); return settings; } const toggleVisibility = (settings) => { settings.style.display = settings.style.display == "none" ? "flex" : "none"; } const callback = (mutationList) => { // Check if the current page is a tweet const currentUrl = window.location.href; if (!re.test(currentUrl)) return; for (const mutation of mutationList) { if (mutation.type !== "attributes") return; // Checks if the buttons doesn't already exist if (exists) { const existingContainers = document.querySelectorAll("#fxtwitterFixupxButtons"); if (existingContainers.length != 0) return; exists = false; } const containers = document.querySelectorAll(".css-175oi2r.r-1iusvr4.r-16y2uox.r-ttdzmv"); if (containers.length == 0) return; // Create container and changes its style const container = document.createElement("DIV"); container.id = "fxtwitterFixupxButtons"; container.style.marginBottom = "15px"; container.style.display = "flex"; container.style.flexDirection = "row"; container.style.gap = "10px"; // Add buttons for (const button of buttons) container.appendChild(button); // Add settings button const settings = createSettings(); document.body.appendChild(settings); const settingsButton = document.createElement("BUTTON"); settingsButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 25px;filter: invert(1);isolation: isolate;mix-blend-mode: difference;"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z"/></svg>`; settingsButton.addEventListener("click", () => toggleVisibility(settings)); settingsButton.style.width = "10%"; settingsButton.style.flexGrow = "1"; settingsButton.style.flexShrink = "0"; settingsButton.style.textAlign = "center"; settingsButton.style.fontFamily = "TwitterChirp"; settingsButton.style.border = "none"; settingsButton.style.borderRadius = "25px"; settingsButton.style.fontWeight = "bold"; container.appendChild(settingsButton); // Insert container containers[0].prepend(container); exists = true; } }; // Create buttons const buttons = []; const totalButtons = Object.keys(ProxyType).map(key => { return savedData[key] ? 1 : 0; }).reduce((a, b) => a + b, 0); Object.keys(ProxyType).forEach(key => { if (savedData[key]) buttons.push(createButton(ProxyType[key])); }); // Displays the buttons when the page is correctly loaded // Options for the observer (which mutations to observe) const config = { attributes: true, subtree: true }; // Create an observer instance linked to the callback function const observer = new MutationObserver(callback); // Start observing the target node for configured mutations observer.observe(document, config); })();