1
0
fuck-twitter/script.js
2024-11-01 21:16:52 +01:00

405 lines
16 KiB
JavaScript

// ==UserScript==
// @name fuck-twitter
// @namespace http://tampermonkey.net/
// @version 1.3
// @match https://x.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Configuration
const SCROLL_INCREMENT_MIN = 100; // Minimum scroll step in pixels
const SCROLL_INCREMENT_MAX = 300; // Maximum scroll step in pixels
const SCROLL_DELAY_MIN = 200; // Minimum delay between scroll steps in ms
const SCROLL_DELAY_MAX = 500; // Maximum delay between scroll steps in ms
const CLICK_DELAY_MIN = 500; // Minimum delay before clicking in ms
const CLICK_DELAY_MAX = 1500; // Maximum delay before clicking in ms
const RETWEET_CONFIRM_DELAY_MIN = 500; // Minimum delay before confirming retweet in ms
const RETWEET_CONFIRM_DELAY_MAX = 1500; // Maximum delay before confirming retweet in ms
// State variables
let isRunning = false;
let isRetweetEnabled = false;
let maxInteractions = 0; // 0 means unlimited
let currentInteractions = 0;
let speedScale = 1; // Default scale factor
let scrollTimeout = null;
let clickTimeout = null;
let retweetConfirmTimeout = null;
// Create Control Panel
const controlPanel = document.createElement('div');
controlPanel.style.position = 'fixed';
controlPanel.style.bottom = '20px';
controlPanel.style.right = '20px';
controlPanel.style.padding = '15px';
controlPanel.style.backgroundColor = 'rgba(255, 255, 255, 0.95)';
controlPanel.style.border = '1px solid #ccc';
controlPanel.style.borderRadius = '8px';
controlPanel.style.zIndex = '1000';
controlPanel.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
controlPanel.style.fontFamily = 'Arial, sans-serif';
controlPanel.style.fontSize = '14px';
controlPanel.style.color = '#333';
controlPanel.style.width = '250px';
// Create Start/Pause Button
const toggleButton = document.createElement('button');
toggleButton.innerText = 'Start Auto Like';
toggleButton.style.marginBottom = '10px';
toggleButton.style.padding = '8px 16px';
toggleButton.style.backgroundColor = '#1DA1F2';
toggleButton.style.color = '#fff';
toggleButton.style.border = 'none';
toggleButton.style.borderRadius = '4px';
toggleButton.style.cursor = 'pointer';
toggleButton.style.width = '100%';
toggleButton.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
controlPanel.appendChild(toggleButton);
// Create Retweet Checkbox
const retweetContainer = document.createElement('div');
retweetContainer.style.display = 'flex';
retweetContainer.style.alignItems = 'center';
retweetContainer.style.marginTop = '10px';
const retweetCheckbox = document.createElement('input');
retweetCheckbox.type = 'checkbox';
retweetCheckbox.id = 'enableRetweet';
retweetCheckbox.style.marginRight = '8px';
const retweetLabel = document.createElement('label');
retweetLabel.htmlFor = 'enableRetweet';
retweetLabel.innerText = 'Enable Retweeting';
retweetContainer.appendChild(retweetCheckbox);
retweetContainer.appendChild(retweetLabel);
controlPanel.appendChild(retweetContainer);
// Create Interaction Limit Input
const interactionContainer = document.createElement('div');
interactionContainer.style.marginTop = '10px';
const interactionLabel = document.createElement('label');
interactionLabel.htmlFor = 'maxInteractions';
interactionLabel.innerText = 'Max Interactions: ';
interactionLabel.style.display = 'block';
interactionLabel.style.marginBottom = '4px';
const interactionInput = document.createElement('input');
interactionInput.type = 'number';
interactionInput.id = 'maxInteractions';
interactionInput.min = '0';
interactionInput.placeholder = '0 for unlimited';
interactionInput.style.width = '100%';
interactionInput.style.padding = '6px';
interactionInput.style.border = '1px solid #ccc';
interactionInput.style.borderRadius = '4px';
interactionContainer.appendChild(interactionLabel);
interactionContainer.appendChild(interactionInput);
controlPanel.appendChild(interactionContainer);
// Create Interaction Counter Display
const counterDisplay = document.createElement('div');
counterDisplay.style.marginTop = '10px';
counterDisplay.style.display = 'none'; // Hidden by default
const counterText = document.createElement('span');
counterText.id = 'interactionCounter';
counterText.innerText = 'Interactions: 0/0';
counterDisplay.appendChild(counterText);
controlPanel.appendChild(counterDisplay);
// Create Speed Scale Input
const speedContainer = document.createElement('div');
speedContainer.style.marginTop = '10px';
const speedLabel = document.createElement('label');
speedLabel.htmlFor = 'speedScale';
speedLabel.innerText = 'Speed Scale: ';
speedLabel.style.display = 'block';
speedLabel.style.marginBottom = '4px';
const speedInput = document.createElement('input');
speedInput.type = 'number';
speedInput.id = 'speedScale';
speedInput.min = '0.5';
speedInput.max = '3';
speedInput.step = '0.1';
speedInput.value = '1';
speedInput.style.width = '100%';
speedInput.style.padding = '6px';
speedInput.style.border = '1px solid #ccc';
speedInput.style.borderRadius = '4px';
speedContainer.appendChild(speedLabel);
speedContainer.appendChild(speedInput);
controlPanel.appendChild(speedContainer);
// Append the Control Panel to the Document Body
document.body.appendChild(controlPanel);
// Event Listener for Toggle Button
toggleButton.addEventListener('click', () => {
isRunning = !isRunning;
toggleButton.innerText = isRunning ? 'Pause Auto Like' : 'Start Auto Like';
toggleButton.style.backgroundColor = isRunning ? '#e0245e' : '#1DA1F2';
if (isRunning) {
// Retrieve and set maximum interactions
const maxInput = parseInt(interactionInput.value, 10);
maxInteractions = isNaN(maxInput) || maxInput < 0 ? 0 : maxInput;
currentInteractions = 0;
updateCounterDisplay();
// Show the counter display if maxInteractions is set
if (maxInteractions > 0) {
counterDisplay.style.display = 'block';
counterText.innerText = `Interactions: ${currentInteractions}/${maxInteractions}`;
} else {
counterDisplay.style.display = 'none';
}
// Retrieve and set speed scale
const speedValue = parseFloat(speedInput.value);
speedScale = isNaN(speedValue) || speedValue <= 0 ? 1 : speedValue;
startScrolling();
} else {
clearTimeout(scrollTimeout);
clearTimeout(clickTimeout);
clearTimeout(retweetConfirmTimeout);
}
});
// Event Listener for Retweet Checkbox
retweetCheckbox.addEventListener('change', (e) => {
isRetweetEnabled = e.target.checked;
console.log('Retweeting Enabled:', isRetweetEnabled);
});
// Event Listener for Speed Scale Input
speedInput.addEventListener('change', (e) => {
const speedValue = parseFloat(e.target.value);
speedScale = isNaN(speedValue) || speedValue <= 0 ? 1 : speedValue;
console.log('Speed Scale set to:', speedScale);
});
// Utility function to get a random integer between min and max (inclusive), multiplied by speedScale
function getRandomIntScaled(min, max) {
const scaledMin = min * speedScale;
const scaledMax = max * speedScale;
return Math.floor(Math.random() * (scaledMax - scaledMin + 1)) + scaledMin;
}
// Function to update the interaction counter display
function updateCounterDisplay() {
counterText.innerText = `Interactions: ${currentInteractions}/${maxInteractions}`;
}
// Function to smoothly scroll down
function startScrolling() {
if (!isRunning) return;
// Check if maximum interactions reached
if (maxInteractions > 0 && currentInteractions >= maxInteractions) {
console.log('Maximum interactions reached. Stopping the script.');
isRunning = false;
toggleButton.innerText = 'Start Auto Like';
toggleButton.style.backgroundColor = '#1DA1F2';
return;
}
// Scroll by a random small increment, scaled by speedScale
const scrollStep = getRandomIntScaled(SCROLL_INCREMENT_MIN, SCROLL_INCREMENT_MAX);
window.scrollBy({
top: scrollStep,
behavior: 'smooth'
});
// After scrolling, check for the target elements
// Delay to allow scrolling to complete
scrollTimeout = setTimeout(() => {
processElements();
}, getRandomIntScaled(SCROLL_DELAY_MIN, SCROLL_DELAY_MAX));
}
// Function to process Like and Retweet buttons
function processElements() {
if (!isRunning) return;
const likeButton = findVisibleLikeButton();
const retweetButton = isRetweetEnabled ? findVisibleRetweetButton() : null;
if (likeButton) {
handleLikeButton(likeButton);
}
if (retweetButton) {
handleRetweetButton(retweetButton);
}
// Continue scrolling if no actions were taken
if (!likeButton && !retweetButton) {
startScrolling();
}
}
// Function to find a <button> with data-testid="like" that is fully visible and not yet clicked
function findVisibleLikeButton() {
const buttons = document.querySelectorAll('button[data-testid="like"]');
for (let btn of buttons) {
if (isElementFullyVisible(btn) && isButtonEligible(btn)) {
return btn;
}
}
return null;
}
// Function to find a <button> with data-testid="retweet" that is fully visible and not yet clicked
function findVisibleRetweetButton() {
const buttons = document.querySelectorAll('button[data-testid="retweet"]');
for (let btn of buttons) {
if (isElementFullyVisible(btn) && isButtonEligible(btn)) {
return btn;
}
}
return null;
}
// Function to handle clicking the Like button
function handleLikeButton(btn) {
// Scroll to make sure the button is fully visible
btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Wait for scrolling to finish
setTimeout(() => {
// Pause for a random short duration before clicking
const clickDelay = getRandomIntScaled(CLICK_DELAY_MIN, CLICK_DELAY_MAX);
clickTimeout = setTimeout(() => {
// Double-check before clicking
if (isButtonEligible(btn)) {
btn.click();
console.log('Like button clicked:', btn);
// Mark the button as clicked to prevent double-clicking
btn.setAttribute('data-autoclicked', 'true');
// Increment interaction counter
incrementInteractions();
}
// Continue the loop
startScrolling();
}, clickDelay);
}, getRandomIntScaled(500, 1000)); // Additional delay to ensure scrolling has completed
}
// Function to handle clicking the Retweet button and confirming
function handleRetweetButton(btn) {
// Scroll to make sure the button is fully visible
btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Wait for scrolling to finish
setTimeout(() => {
// Pause for a random short duration before clicking
const clickDelay = getRandomIntScaled(CLICK_DELAY_MIN, CLICK_DELAY_MAX);
clickTimeout = setTimeout(() => {
// Double-check before clicking
if (isButtonEligible(btn)) {
btn.click();
console.log('Retweet button clicked:', btn);
// Mark the button as clicked to prevent double-clicking
btn.setAttribute('data-autoclicked', 'true');
// Increment interaction counter
incrementInteractions();
// After clicking retweet, wait and confirm
retweetConfirmTimeout = setTimeout(() => {
const confirmButton = findRetweetConfirmButton();
if (confirmButton) {
confirmButton.click();
console.log('Retweet confirmed:', confirmButton);
// Mark the confirm button as clicked
confirmButton.setAttribute('data-autoclicked', 'true');
// Increment interaction counter
incrementInteractions();
}
// Continue the loop
startScrolling();
}, getRandomIntScaled(RETWEET_CONFIRM_DELAY_MIN, RETWEET_CONFIRM_DELAY_MAX));
} else {
// Continue the loop
startScrolling();
}
}, clickDelay);
}, getRandomIntScaled(500, 1000)); // Additional delay to ensure scrolling has completed
}
// Function to find the Retweet Confirm button
function findRetweetConfirmButton() {
const buttons = document.querySelectorAll('div[data-testid="retweetConfirm"]');
for (let btn of buttons) {
if (isElementFullyVisible(btn) && isButtonEligible(btn)) {
return btn;
}
}
return null;
}
// Function to check if an element is fully visible in the viewport
function isElementFullyVisible(el) {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// Function to check if the button is eligible for clicking
function isButtonEligible(btn) {
// Check if the button has already been clicked by this script
if (btn.getAttribute('data-autoclicked') === 'true') {
return false;
}
// Ensure the button is still the correct type ('like', 'retweet', or 'retweetConfirm')
const currentTestId = btn.getAttribute('data-testid');
if (currentTestId !== 'like' && currentTestId !== 'retweet' && currentTestId !== 'retweetConfirm') {
return false;
}
return true;
}
// Function to increment interactions and update the counter
function incrementInteractions() {
currentInteractions += 1;
if (maxInteractions > 0) {
counterText.innerText = `Interactions: ${currentInteractions}/${maxInteractions}`;
if (currentInteractions >= maxInteractions) {
console.log('Maximum interactions reached. Stopping the script.');
isRunning = false;
toggleButton.innerText = 'Start Auto Like';
toggleButton.style.backgroundColor = '#1DA1F2';
clearTimeout(scrollTimeout);
clearTimeout(clickTimeout);
clearTimeout(retweetConfirmTimeout);
}
}
}
// Optional: Observe DOM changes to handle dynamically loaded content
const observer = new MutationObserver((mutations) => {
if (isRunning) {
// You can trigger actions based on specific mutations if needed
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();