您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
See (a very VERY rough idea of) how much money a post is worth.
// ==UserScript== // @name PayCheck for X (Formerly Twitter) // @description See (a very VERY rough idea of) how much money a post is worth. // @version 0.0.5 // @author yungsamd17 & Theo @t3dotgg // @namespace https://github.com/yungsamd17/paycheck-userscript // @icon https://raw.githubusercontent.com/yungsamd17/paycheck-userscript/main/assets/paycheck-for-twitter.png // @match https://twitter.com/* // @match https://mobile.twitter.com/* // @match https://tweetdeck.twitter.com/* // @match https://x.com/* // @grant none // @run-at document-end // ==/UserScript== function convertToRawCount(internationalInputString) { const numberPattern = /([\d,.]+)([kmb]*)/i; const matches = internationalInputString.match(numberPattern); if (!matches) { return NaN; // Return NaN if the input doesn't match the expected pattern } const numericPart = matches[1]; const multiplier = matches[2].toLowerCase(); let numericValue; const lastChars = [ numericPart.slice(-1), numericPart.slice(-2, -1), numericPart.slice(-3, -2), ]; // Check if second or third to last character are , or . to handle international numbers if (lastChars.includes(".") || lastChars.includes(",")) { const parts = numericPart.replace(",", ".").split("."); const integerPart = parts[0].replace(/[,]/g, ""); const decimalPart = parts[1] ? parts[1] : "0"; numericValue = parseFloat(integerPart + "." + decimalPart); } else { numericValue = parseFloat(numericPart.replaceAll(",", "")); } let factor = 1; switch (multiplier) { case "k": factor = 1000; break; case "m": factor = 1000000; break; case "b": factor = 1000000000; break; } return Math.round(numericValue * factor); } function convertToDollars(number) { const rawCount = convertToRawCount(number); const processed = rawCount * 0.000026; if (processed < 0.1) return processed.toFixed(5); return processed.toFixed(2); } const globalSelectors = {}; globalSelectors.postCounts = `[role="group"][id*="id__"]:only-child`; globalSelectors.articleDate = `[role="article"][aria-labelledby*="id__"][tabindex="-1"] time`; globalSelectors.analyticsLink = " :not(.dollarBox)>a[href*='/analytics']"; globalSelectors.viewCount = globalSelectors.postCounts + globalSelectors.analyticsLink; const innerSelectors = {}; innerSelectors.dollarSpot = "div div:first-child"; innerSelectors.viewSVG = "div div:first-child svg"; innerSelectors.viewAmount = "div div:last-child span span span"; innerSelectors.articleViewAmount = "span div:first-child span span span"; function doWork() { const viewCounts = Array.from( document.querySelectorAll(globalSelectors.viewCount) ); const articleViewDateSections = document.querySelectorAll(globalSelectors.articleDate); if (articleViewDateSections.length) { // the rootDateViewsSection will always be the parent->parent->parent of the last element of the articleDate querySelectorAll result let rootDateViewsSection = articleViewDateSections[articleViewDateSections.length - 1].parentElement.parentElement.parentElement; // if there is one child, that means it's an old tweet with no viewcount // if there are more than 4, we already added the paycheck value if (rootDateViewsSection?.children?.length !== 1 && rootDateViewsSection?.children.length < 4) { // clone 2nd and 3rd child of rootDateViewsSection const clonedDateViewSeparator = rootDateViewsSection?.children[1].cloneNode(true); const clonedDateView = rootDateViewsSection?.children[2].cloneNode(true); // insert clonedDateViews and clonedDateViewsTwo after the 3rd child we just cloned rootDateViewsSection?.insertBefore( clonedDateViewSeparator, rootDateViewsSection?.children[2].nextSibling ); rootDateViewsSection?.insertBefore( clonedDateView, rootDateViewsSection?.children[3].nextSibling ); // get view count value from 'clonedDateViewsTwo' const viewCountValue = clonedDateView?.querySelector( innerSelectors.articleViewAmount )?.textContent; const dollarAmount = convertToDollars(viewCountValue); // replace textContent in cloned clonedDateViews (now 4th child) with converted view count value clonedDateView.querySelector( innerSelectors.articleViewAmount ).textContent = "$" + dollarAmount; // remove 'views' label clonedDateView.querySelector(`span`).children[1].remove(); } } for (const view of viewCounts) { // only add the dollar box once if (!view.classList.contains("replaced")) { // make sure we don't touch this one again view.classList.add("replaced"); // get parent and clone to make dollarBox const parent = view.parentElement; const dollarBox = parent.cloneNode(true); dollarBox.classList.add("dollarBox"); // insert dollarBox after view count parent.parentElement.insertBefore(dollarBox, parent.nextSibling); // remove view count icon const oldIcon = dollarBox.querySelector(innerSelectors.viewSVG); oldIcon?.remove(); // swap the svg for a dollar sign const dollarSpot = dollarBox.querySelector(innerSelectors.dollarSpot) ?.firstChild?.firstChild; dollarSpot.textContent = "$"; // magic alignment value dollarSpot.style.marginTop = "-0.6rem"; } // get the number of views and calculate & set the dollar amount const dollarBox = view.parentElement.nextSibling.firstChild; const viewCount = view.querySelector( innerSelectors.viewAmount )?.textContent; if (viewCount == undefined) continue; const dollarAmountArea = dollarBox.querySelector(innerSelectors.viewAmount); dollarAmountArea.textContent = convertToDollars(viewCount); } } function throttle(func, limit) { let lastFunc; let lastRan; return function () { const context = this; const args = arguments; if (!lastRan) { func.apply(context, args); lastRan = Date.now(); } else { clearTimeout(lastFunc); lastFunc = setTimeout(function () { if (Date.now() - lastRan >= limit) { func.apply(context, args); lastRan = Date.now(); } }, limit - (Date.now() - lastRan)); } }; } // Function to start MutationObserver const observe = () => { const runDocumentMutations = throttle(() => { requestAnimationFrame(doWork); }, 1000); const observer = new MutationObserver((mutationsList) => { if (!mutationsList.length) return; runDocumentMutations(); }); observer.observe(document, { childList: true, subtree: true, }); }; observe();