Skip to content

Instantly share code, notes, and snippets.

@GONZOsint
Created May 3, 2024 09:34
Show Gist options
  • Save GONZOsint/3cc0c9aa1acd78aa56f67cddcd6e5394 to your computer and use it in GitHub Desktop.
Save GONZOsint/3cc0c9aa1acd78aa56f67cddcd6e5394 to your computer and use it in GitHub Desktop.
// ==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