Skip to content

Instantly share code, notes, and snippets.

@consoleSkunk
Forked from busybox11/twitterblue-nerd.js
Last active February 15, 2024 16:42
Show Gist options
  • Save consoleSkunk/f30cca3a63c1c28e276f7dacb7255b0a to your computer and use it in GitHub Desktop.
Save consoleSkunk/f30cca3a63c1c28e276f7dacb7255b0a to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name @chaoticvibing Twitter Blue Nerd - twitter.com
// @namespace Violentmonkey Scripts
// @match *://*.twitter.com/*
// @grant none
// @version 1.9.0
// @author @chaoticvibing - GH @busybox11
// @description 11/9/2022, 11:45:28 PM
// @updateURL https://gist.githubusercontent.com/Enbika/f30cca3a63c1c28e276f7dacb7255b0a/raw/twitterblue-nerd.js
// @downloadURL https://gist.githubusercontent.com/Enbika/f30cca3a63c1c28e276f7dacb7255b0a/raw/twitterblue-nerd.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
// 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.6.0 PATH-BASED BLUE CHECK DETECTION
// 1.7.0 SOMEHOW FIXES NEWER FIREFOX VERSIONS
// 1.7.1 FIREFOX AGAIN...
// 1.8.0 TWITTER CHANGED STUFF AGAIN PLEASE SEND HELP
// 1.9.0 UPDATED POPUP BECAUSE TWITTER (elon) HAVE DECIDED TO BE DUMB (thanks @tom-wharf for the update!)
/*
* 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)
*
*/
// STOLEN FROM https://twitter.com/shadowbIood/status/1590462560515473409?s=20&t=AmfQmmFgdpKOsPqnoawjVQ
const nerdtick = `
<g><path d="M16.5 3H2v18h15c3.038 0 5.5-2.46 5.5-5.5 0-1.4-.524-2.68-1.385-3.65-.08-.09-.089-.22-.023-.32.574-.87.908-1.91.908-3.03C22 5.46 19.538 3 16.5 3zm-.796 5.99c.457-.05.892-.17 1.296-.35-.302.45-.684.84-1.125 1.15.004.1.006.19.006.29 0 2.94-2.269 6.32-6.421 6.32-1.274 0-2.46-.37-3.459-1 .177.02.357.03.539.03 1.057 0 2.03-.35 2.803-.95-.988-.02-1.821-.66-2.109-1.54.138.03.28.04.425.04.206 0 .405-.03.595-.08-1.033-.2-1.811-1.1-1.811-2.18v-.03c.305.17.652.27 1.023.28-.606-.4-1.004-1.08-1.004-1.85 0-.4.111-.78.305-1.11 1.113 1.34 2.775 2.22 4.652 2.32-.038-.17-.058-.33-.058-.51 0-1.23 1.01-2.22 2.256-2.22.649 0 1.235.27 1.647.7.514-.1.997-.28 1.433-.54-.168.52-.526.96-.992 1.23z"></path></g>
`
const blueverified = `
<g><path d="M12 1.75c-1.43 0-2.67.88-3.33 2.19-1.4-.46-2.91-.2-3.92.81-1.01 1.01-1.26 2.52-.8 3.91-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91 1.01 1.01 2.52 1.26 3.91.81.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81 1.01-1.01 1.27-2.52.81-3.91 1.31-.67 2.19-1.91 2.19-3.34s-.88-2.67-2.19-3.34c.46-1.39.2-2.9-.81-3.91-1.01-1.01-2.52-1.27-3.91-.81-.66-1.31-1.91-2.19-3.34-2.19zm2.117 5.85c.65 0 1.235.27 1.647.7.514-.1.997-.28 1.433-.54-.168.52-.526.96-.992 1.23.456-.05.892-.17 1.295-.35a4.41 4.41 0 0 1-1.125 1.15c.004.1.006.19.006.29 0 2.94-2.27 6.32-6.422 6.32-1.274 0-2.46-.37-3.459-1 .177.02.357.03.54.03a4.55 4.55 0 0 0 2.802-.95 2.259 2.259 0 0 1-2.108-1.54c.138.03.28.04.424.04.206 0 .406-.03.596-.08a2.227 2.227 0 0 1-1.81-2.18v-.03c.304.17.65.27 1.02.28a2.218 2.218 0 0 1-1.003-1.85c0-.4.11-.78.305-1.11a6.48 6.48 0 0 0 4.652 2.32 2.263 2.263 0 0 1-.057-.51c0-1.23 1.01-2.22 2.256-2.22z"/></g>
`
let regularVerifiedPath = 'svg path[d^="M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z"]'
let otherRegularVerifiedPath = 'svg path[d^="M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z"]'
const regulartick = `
<g><path d="M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z"></path></g>
`
// EDITED, STOLEN FROM https://stackoverflow.com/questions/70507318/how-to-get-react-element-props-from-html-element-with-javascript
function getReactProps(parent, target) {
parent = parent.wrappedJSObject ?? parent;
target = target.wrappedJSObject ?? target;
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 && !props.isVerified) {
elem.innerHTML = nerdtick
} else if (props.isBlueVerified && props.isVerified) {
elem.innerHTML = blueverified
} else {
elem.innerHTML = regulartick
}
}
function bluetickHandling(bluetick) {
let propsElem = getReactProps(bluetick.parentElement.parentElement, bluetick.parentElement)
if (propsElem.children !== undefined) {
let props;
try {
props = propsElem.children[0][0].props
} catch(e) {
propsElem = getReactProps(bluetick.parentElement.parentElement.parentElement.parentElement, bluetick.parentElement.parentElement.parentElement)
props = propsElem.children[0][0].props
sc.log(props)
}
if (props.isBlueVerified !== undefined) {
updateBlueTick(bluetick, props)
} else {
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) {
// SVG PATH DETECTION
// Thanks GH @artesea - https://gist.github.com/busybox11/53c76f57a577a47a19fab649a76f18e3?permalink_comment_id=4366043#gistcomment-4366043
// Author of this snippet
let blueticksPath = elem.querySelectorAll(regularVerifiedPath)
let differentBlueticksPath = elem.querySelectorAll(otherRegularVerifiedPath)
const blueTicks = [
...blueticksPath,
...differentBlueticksPath
]
for (let bluetick of blueTicks.flat()) {
try {
if (bluetick !== null) {
bluetickHandling(bluetick.parentElement.parentElement)
}
} catch(e) {sc.log("blueTicks check", bluetick, e)}
}
// PROFILE POPUPS
const profileBlueticks = elem.querySelectorAll('.css-1dbjc4n.r-xoduu5.r-1pcd2l5')
try {
for (let profileBluetick of profileBlueticks) {
if (profileBluetick !== null) {
let tickChild = profileBluetick.firstChild
let tickChildProps = getReactProps(profileBluetick.parentElement, profileBluetick)
updateBlueTick(tickChild, tickChildProps)
}
}
} catch(e) {sc.log(e)}
}
}
} catch(e) {sc.log(e)}
}
const sc = {
log: (...args) => {
console.log('[nerdtick]', ...args)
}
}
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