Created
May 3, 2024 09:34
-
-
Save GONZOsint/3cc0c9aa1acd78aa56f67cddcd6e5394 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 AgentX | |
// @namespace Social Agents | |
// @description Enhances Twitter web interface by providing functionalities like copying profile URLs and tweet content, viewing profiles on the Wayback Machine, and highlighting specific text patterns within tweets. | |
// @match https://twitter.com/* | |
// @author GONZO | |
// ==/UserScript== | |
document.addEventListener('DOMContentLoaded', initializeExtension); | |
function initializeExtension() { | |
const twitterTweetSelector = 'article[role="article"]'; | |
observeDOMChanges(twitterTweetSelector, handleTwitterTweets); | |
addHighlightSwitch(); | |
function observeDOMChanges(selector, callback) { | |
const observer = new MutationObserver(() => { | |
callback(selector); | |
if (document.querySelector('.switch input[type="checkbox"]').checked) { | |
highlightNewTweets(); | |
} | |
}); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
callback(selector); | |
} | |
function handleTwitterTweets(selector) { | |
const tweets = document.querySelectorAll(selector); | |
tweets.forEach(tweet => { | |
if (tweet.getAttribute('data-testid') === 'notification') { | |
return; | |
} | |
if (!tweet.querySelector('.custom-btn-group')) { | |
injectButtons(tweet); | |
} | |
}); | |
} | |
function injectButtons(tweet) { | |
const profileUrl = getProfileUrl(tweet); | |
const tweetContent = getTweetContent(tweet); | |
const profileUrlButton = createButton(profileIcon(), 'Copy Profile URL', () => copyToClipboard(profileUrl, "Profile URL copied!")); | |
const copyContentButton = createButton(textIcon(), 'Copy Tweet Content', () => copyToClipboard(tweetContent, "Content copied!")); | |
const waybackButton = createButton(waybackIcon(), 'View Profile on Wayback Machine', () => openWaybackMachine(profileUrl)); | |
const buttonGroup = document.createElement('div'); | |
buttonGroup.className = 'custom-btn-group'; | |
buttonGroup.style.display = 'flex'; | |
buttonGroup.style.flexDirection = 'column'; | |
buttonGroup.style.alignItems = 'flex-start'; | |
buttonGroup.style.marginTop = '20px'; | |
buttonGroup.appendChild(profileUrlButton); | |
buttonGroup.appendChild(copyContentButton); | |
buttonGroup.appendChild(waybackButton); | |
tweet.appendChild(buttonGroup); | |
} | |
function createButton(svgIcon, ariaLabel, onClickHandler) { | |
const button = document.createElement('button'); | |
button.className = 'btn custom-btn'; | |
button.innerHTML = svgIcon; | |
button.setAttribute('aria-label', ariaLabel); | |
button.style.margin = '2px 10px'; | |
button.style.border = '1px solid black'; | |
button.style.background = 'white'; | |
button.style.color = 'black'; | |
button.style.padding = '4px 10px'; | |
button.style.cursor = 'pointer'; | |
button.addEventListener('click', onClickHandler); | |
return button; | |
} | |
function addHighlightSwitch() { | |
const switchContainer = document.createElement('label'); | |
switchContainer.className = 'switch'; | |
switchContainer.style.position = 'fixed'; | |
switchContainer.style.bottom = '20px'; | |
switchContainer.style.left = '20px'; | |
const switchInput = document.createElement('input'); | |
switchInput.type = 'checkbox'; | |
switchInput.addEventListener('change', handleSwitchChange); | |
const switchSlider = document.createElement('span'); | |
switchSlider.className = 'slider round'; | |
const switchIcon = document.createElement('span'); | |
switchIcon.style.fontFamily = 'Arial, sans-serif'; | |
switchIcon.innerHTML = 'Highlight'; | |
switchContainer.appendChild(switchIcon); | |
switchContainer.appendChild(switchInput); | |
switchContainer.appendChild(switchSlider); | |
document.body.appendChild(switchContainer); | |
if (switchInput.checked) { | |
highlightContent(); | |
} | |
} | |
function handleSwitchChange(event) { | |
const switchState = event.target.checked; | |
if (switchState) { | |
highlightContent(); | |
} else { | |
removeHighlight(); | |
} | |
} | |
function highlightContent() { | |
const tweets = document.querySelectorAll(twitterTweetSelector); | |
tweets.forEach(tweet => { | |
const tweetContent = getTweetContent(tweet); | |
const highlightedContent = highlightMatchedText(tweetContent); | |
tweet.querySelector('[lang]').innerHTML = highlightedContent; | |
}); | |
} | |
function removeHighlight() { | |
const tweets = document.querySelectorAll(twitterTweetSelector); | |
tweets.forEach(tweet => { | |
const tweetContent = getTweetContent(tweet); | |
tweet.querySelector('[lang]').innerHTML = tweetContent; | |
}); | |
} | |
function copyToClipboard(text, message) { | |
navigator.clipboard.writeText(text).then(() => { | |
showNotification(message); | |
}).catch(err => { | |
showNotification('Failed to copy text.', true); | |
}); | |
} | |
function showNotification(message, error = false) { | |
const notification = document.createElement('div'); | |
notification.textContent = message; | |
notification.style.position = 'fixed'; | |
notification.style.bottom = '20px'; | |
notification.style.left = '20px'; | |
notification.style.padding = '10px'; | |
notification.style.fontFamily = 'Arial, sans-serif'; | |
notification.style.color = 'white'; | |
notification.style.backgroundColor = error ? '#FFC470' : '#8576FF'; | |
notification.style.borderRadius = '5px'; | |
notification.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)'; | |
notification.style.zIndex = '1000'; | |
document.body.appendChild(notification); | |
setTimeout(() => { | |
notification.style.opacity = '0'; | |
setTimeout(() => notification.remove(), 500); | |
}, 2000); | |
} | |
function openWaybackMachine(url) { | |
const waybackUrl = `https://web.archive.org/web/*/${url}`; | |
window.open(waybackUrl, '_blank'); | |
} | |
function getProfileUrl(tweet) { | |
const profileLink = tweet.querySelector('[data-testid^="UserAvatar-Container-"]'); | |
if (profileLink) { | |
const username = profileLink.getAttribute('data-testid').replace('UserAvatar-Container-', ''); | |
return `https://twitter.com/${username}`; | |
} else { | |
return ''; | |
} | |
} | |
function getTweetContent(tweet) { | |
const content = tweet.querySelector('[lang]'); | |
return content ? content.textContent : ''; | |
} | |
function highlightMatchedText(tweetContent) { | |
const nameRegex = /(?<!^|\n|^ |\.\s|\!\s|\?\s|\.\s\s|\:\s|\-\s|\:\s\s|\;\s|\•\s|\.\.\s|\-\s|[!?$-:•;@#]|[\r\n])\b(?!I\b)(?:[A-Z](?![A-Z]*\b(?:Dr|Mr|Mrs|Prof|Hon)\b|\b(?:\d+|\S+\.\S+@\S+\.\S+|\S+\.\S+)\b)(?<!\.)[a-zA-Z.'-]*\s*){1,4}(?:(?:Jr|Sr|Mr|MR)\.?|\b)\b/g | |
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g; | |
const phoneRegex = /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g; | |
const nameColor = '#8576FF'; | |
const emailColor = '#FFC470'; | |
const phoneColor = '#DD5746'; | |
function createStyledSpan(match, color) { | |
return `<span style="font-weight: bold; color: ${color};">${match}</span>`; | |
} | |
tweetContent = tweetContent.replace(/<span\b[^>]*>(.*?)<\/span>/gi, '$1'); | |
let highlightedContent = tweetContent.replace(nameRegex, match => createStyledSpan(match, nameColor)); | |
highlightedContent = highlightedContent.replace(emailRegex, match => createStyledSpan(match, emailColor)); | |
highlightedContent = highlightedContent.replace(phoneRegex, match => createStyledSpan(match, phoneColor)); | |
return highlightedContent; | |
} | |
function highlightNewTweets() { | |
const tweets = document.querySelectorAll(`${twitterTweetSelector}:not(.highlighted)`); | |
tweets.forEach(tweet => { | |
tweet.classList.add('highlighted'); | |
const tweetContent = getTweetContent(tweet); | |
const highlightedContent = highlightMatchedText(tweetContent); | |
tweet.querySelector('[lang]').innerHTML = highlightedContent; | |
}); | |
} | |
} | |
function profileIcon() { | |
return '<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16px" height="16px"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>'; | |
} | |
function textIcon() { | |
return '<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16px" height="16px"><path d="M 4 4 L 4 6 L 20 6 L 20 4 L 4 4 z M 4 8 L 4 10 L 20 10 L 20 8 L 4 8 z M 4 12 L 4 14 L 20 14 L 20 12 L 4 12 z"/></svg>'; | |
} | |
function waybackIcon() { | |
return '<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16px" height="16px"><text x="50%" y="80%" alignment-baseline="middle" text-anchor="middle" font-size="12" font-weight="bold">WM</text></svg>'; | |
} | |
initializeExtension(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment