Skip to content

Instantly share code, notes, and snippets.

@arturo182
Forked from busybox11/twitterblue-nerd.js
Last active January 29, 2023 21:10
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arturo182/85251cbc370a12d9de6766dd44b88d5c to your computer and use it in GitHub Desktop.
Save arturo182/85251cbc370a12d9de6766dd44b88d5c to your computer and use it in GitHub Desktop.
// ==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