x-twitter-add-to-list-button

1-click "add user to [xyz] list" button next to usernames while scrolling your x (twitter) feed (be sure to edit the variable "lists")

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name           x-twitter-add-to-list-button
// @name:ja        x-twitter-add-to-list-button
// @namespace      x-twitter
// @version        0.2.2
// @description    1-click "add user to [xyz] list" button next to usernames while scrolling your x (twitter) feed (be sure to edit the variable "lists")
// @description:ja リストにワンクリックで追加するボタンを表示します(変数"lists"を必ず編集してください)
// @author         fuwawascoco
// @match          https://twitter.com/*
// @match          https://mobile.twitter.com/*
// @match          https://x.com/*
// @icon           https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant          none
// @license        MIT
// ==/UserScript==

(function() {
    'use strict';

    const lists = ['waitlist', 'illustrators', 'animators']; // be sure to change to the NAME of your lists (not IDs)
    const checkInterval = 512; // ms
    const tryInterval = 64; // ms
    const maxRetries = 100; // maximum number of retries to avoid infinite loops

    function createNotification(message) {
        const notification = document.createElement('div');
        notification.style.position = 'fixed';
        notification.style.bottom = '10px';
        notification.style.right = '10px';
        notification.style.padding = '10px';
        notification.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
        notification.style.color = 'white';
        notification.style.borderRadius = '5px';
        notification.style.zIndex = '10000';
        notification.textContent = message;
        document.body.appendChild(notification);
        setTimeout(() => {
            document.body.removeChild(notification);
        }, 5000);
    }

    function retryUntilSuccess(newTab, selector, innerText, callback) {
        let attempts = 0;
        const intervalID = setInterval(() => {
            let element;
            const elements = newTab.document.querySelectorAll(selector);
            if (innerText != '') {
                element = Array.from(elements).find(el => el.textContent.trim() === innerText);
            } else {
                element = elements[0];
            }
            if (element && element.offsetParent != null) { // Check if element is visible
                clearInterval(intervalID);
                callback(element);
            }
            if (++attempts >= maxRetries) {
                clearInterval(intervalID);
                console.error(`Failed to find element: ${selector} with innerText: ${innerText}`);
                createNotification(`"${innerText}" was not found, please edit the script to update the variable "lists" with your own names.`);
                newTab.close();
            }
        }, tryInterval);
    }

    function onClick(userProfile, list) {
        const newTab = open(userProfile);
        newTab.addEventListener('beforeunload', () => clearInterval(intervalID));

        retryUntilSuccess(newTab, 'button[aria-label="More"][data-testid="userActions"]', '', moreButton => {
            moreButton.click();
            retryUntilSuccess(newTab, 'a[href="/i/lists/add_member"][role="menuitem"]', '', listButton => {
                listButton.click();
                retryUntilSuccess(newTab, '[aria-modal="true"]', '', modal => {
                    let attempts = 0;
                    const intervalID = setInterval(() => {
                        const listSpan = Array.from(modal.getElementsByTagName('span')).find(span => span.textContent === list);
                        if (listSpan) {
                            clearInterval(intervalID);
                            const checkbox = listSpan.closest('[role="checkbox"]');
                            if (checkbox && checkbox.getAttribute('aria-checked') === 'false') {
                                checkbox.click();
                            }
                        } else if (++attempts >= maxRetries) {
                            clearInterval(intervalID);
                            createNotification(`"${list}" was not found, please edit the script to update the variable "lists" with your own names.`);
                            newTab.close();
                        }
                    }, tryInterval);
                });
            });
        });
    }

    function createButton(userProfile, list) {
        const button = document.createElement('button');
        button.style.fontSize = '90%';
        button.style.margin = '0 0.25em';
        button.textContent = list;
        button.addEventListener('click', () => onClick(userProfile, list));
        return button;
    }

    function createButtonContainer(userProfile) {
        const container = document.createElement('div');
        container.style.position = 'relative';
        container.style.left = '2%';
        container.style.opacity = 0.5;
        lists.forEach(list => container.appendChild(createButton(userProfile, list)));
        container.classList.add('listButtons');
        return container;
    }

    function addButtons() {
        const nodes = document.querySelectorAll('[data-testid="User-Name"]:not(:has(.listButtons))');
        nodes.forEach(node => {
            const userProfile = node.querySelector('a')?.href || '';
            if (userProfile) {
                node.appendChild(createButtonContainer(userProfile));
            }
        });
    }

    setInterval(addButtons, checkInterval);
})();