-
-
Save arturo182/85251cbc370a12d9de6766dd44b88d5c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name @chaoticvibing Twitter Blue red ticks - twitter.com | |
// @namespace Violentmonkey Scripts | |
// @match *://*.twitter.com/* | |
// @grant none | |
// @version 1.5.2 | |
// @author @chaoticvibing - GH @busybox11, modified by @arturo182 | |
// @description 11/9/2022, 11:45:28 PM | |
// @updateURL https://gist.githubusercontent.com/arturo182/85251cbc370a12d9de6766dd44b88d5c/raw/twitterblue-red.user.js | |
// @downloadURL https://gist.githubusercontent.com/arturo182/85251cbc370a12d9de6766dd44b88d5c/raw/twitterblue-red.user.js | |
// ==/UserScript== | |
// YOU'RE FREE TO DO WHATEVER YOU WANT WITH THIS SCRIPT BUT IF YOU DO MAKE SOMETHING | |
// PLEASE MAKE SURE TO MENTION ME SOMEWHERE - I hope you'll understand why :) | |
// Also https://paypal.me/busybox11 because I am broke | |
// Commissions on https://uncove.com/busybox11 | |
/* INSTRUCTIONS | |
* | |
* - Install a userscript browser extension | |
* (I used ViolentMonkey https://chrome.google.com/webstore/detail/violentmonkey/jinjaccalgkegednnccohejagnlnfdag, | |
* but you can use any extension you want, such as tampermonkey, it should work fine) | |
* FIREFOX USERS: It seems to work better with TamperMonkey: https://addons.mozilla.org/fr/firefox/addon/tampermonkey/ | |
* - Import the script | |
* On ViolentMonkey, click on the extension icon, then gear icon (Open dashboard) | |
* There should be a plus icon on the top left hand corner, click on it and select Install from URL | |
* Use this URL: https://gist.githubusercontent.com/busybox11/53c76f57a577a47a19fab649a76f18e3/raw/twitterblue-nerd.js | |
* It should now work and update by itself | |
* | |
*/ | |
/* | |
* DISCLAIMER | |
* I made this in a rush because of a challenge I convinced myself to do in reply to a tweet: | |
* https://twitter.com/Quinten0508/status/1590464705822224384?s=20&t=R_KhoR4a-_3fI4n4mbmmGA | |
* It might have horrible performance and it could not be reliable as I've tested this very quickly | |
* on some places I could find Twitter blue checkmarks, but I haven't made much research on it. | |
* At least it runs fine on my Ryzen 9 5900HS laptop and I don't see any noticeable frame drops | |
* on my 165Hz QHD display since I made this script, which might be a sign it's not impacting much. | |
* (I don't care anyway, fell free to modify it if it isn't) | |
*/ | |
// 1.1.0 ALSO UPDATE ON HEADER OF PROFILE | |
// 1.1.1 AUTO UPDATE | |
// 1.1.2 Better handling of verified notifications | |
// 1.1.3 Better error logging | |
// 1.2.0 INITIAL VERY WIP FIREFOX SUPPORT | |
// 1.2.1 Misc code quality changes | |
// 1.3.0 MADE IT WORK WITH THEORETICALLY ALL NOTIFICATIONS | |
// 1.4.0 ALSO SHOW ON PROFILE VERIFICATION POPUP | |
// 1.5.0 WIP SHOULD WORK ON ALL LANGUAGES | |
// 1.5.1 Changed nerd icon to red ticks | |
// 1.5.2 Blue and Verified accounts have a red to blue gradient on their tick | |
// STOLEN FROM https://stackoverflow.com/questions/70507318/how-to-get-react-element-props-from-html-element-with-javascript | |
function getReactProps(parent, target) { | |
// INITIAL VERY WIP FIREFOX HANDLING | |
parent = (window.chrome) ? parent : parent.wrappedJSObject; | |
target = (window.chrome) ? target : target.wrappedJSObject; | |
const keyof_ReactProps = Object.keys(parent).find(k => k.startsWith("__reactProps$")); | |
const symof_ReactFragment = Symbol.for("react.fragment"); | |
//Find the path from target to parent | |
let path = []; | |
let elem = target; | |
while (elem !== parent) { | |
let index = 0; | |
for (let sibling = elem; sibling != null;) { | |
if (sibling[keyof_ReactProps]) index++; | |
sibling = sibling.previousElementSibling; | |
} | |
path.push({ child: elem, index }); | |
elem = elem.parentElement; | |
} | |
//Walk down the path to find the react state props | |
let state = elem[keyof_ReactProps]; | |
for (let i = path.length - 1; i >= 0 && state != null; i--) { | |
//Find the target child state index | |
let childStateIndex = 0, childElemIndex = 0; | |
while (childStateIndex < state.children.length) { | |
let childState = state.children[childStateIndex]; | |
if (childState instanceof Object) { | |
//Fragment children are inlined in the parent DOM element | |
let isFragment = childState.type === symof_ReactFragment && childState.props.children.length; | |
childElemIndex += isFragment ? childState.props.children.length : 1; | |
if (childElemIndex === path[i].index) break; | |
} | |
childStateIndex++; | |
} | |
let childState = state.children[childStateIndex] ?? (childStateIndex === 0 ? state.children : null); | |
state = childState?.props; | |
elem = path[i].child; | |
} | |
return state; | |
} | |
function updateBlueTick(elem, props) { | |
if (props.isBlueVerified) { | |
if (props.isVerified) { | |
elem.innerHTML = '<defs><linearGradient id="red2blue"><stop offset="0%" stop-color="red"></stop><stop offset="100%" stop-color="#1D9BF0"></stop></linearGradient></defs>' + elem.innerHTML | |
elem.style.fill = "url('#red2blue')"; | |
} else { | |
elem.style.color = 'red' | |
} | |
} | |
} | |
function bluetickHandling(bluetick) { | |
let propsElem = getReactProps(bluetick.parentElement, bluetick) | |
if (propsElem.children !== undefined) { | |
const props = propsElem.children[0][0].props | |
if (props.isBlueVerified !== undefined) { | |
updateBlueTick(bluetick, props) | |
} else { | |
// VERY HACKY FIX DO BETTER NEXT TIME | |
const otherProps = propsElem.children[0][propsElem.children[0].length - 1].props | |
updateBlueTick(bluetick, otherProps) | |
} | |
} else { | |
const propsElemParent = getReactProps(bluetick.parentElement.parentElement.parentElement, bluetick.parentElement.parentElement) | |
const propsParent = propsElemParent.children[0][0].props | |
updateBlueTick(bluetick, propsParent) | |
} | |
} | |
function handleMutation(mutations) { | |
try { | |
for (let mutation of mutations) { | |
for (let elem of mutation.addedNodes) { | |
// GENERAL TWEETS WIP | |
const blueticks = elem.querySelectorAll('.r-13v1u17.r-4qtqp9.r-yyyyoo.r-1xvli5t') | |
try { | |
for (let bluetick of blueticks) { | |
if (bluetick !== null) { | |
bluetickHandling(bluetick) | |
} | |
} | |
} catch(e) {console.log(e)} | |
// PROFILE POPUPS | |
const profileBlueticks = elem.querySelectorAll('.css-1dbjc4n.r-xoduu5.r-1pcd2l5') | |
try { | |
for (let profileBluetick of profileBlueticks) { | |
if (profileBluetick !== null) { | |
if (profileBluetick.lastChild.firstChild.innerText.includes('Twitter Blue')) { | |
updateBlueTick(profileBluetick.firstChild, {isBlueVerified: true, isVerified: false}) | |
} | |
} | |
} | |
} catch(e) {console.log(e)} | |
// ENGLISH SPECIFIC | |
const blueticksEng = elem.querySelectorAll('[aria-label="Verified account"]') | |
try { | |
for (let bluetick of blueticksEng) { | |
if (bluetick !== null) { | |
bluetickHandling(bluetick) | |
} | |
} | |
} catch(e) {console.log(e)} | |
} | |
} | |
} catch(e) {} | |
} | |
const observer = new MutationObserver(handleMutation) | |
observer.observe(document, { childList: true, subtree: true }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment