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")

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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);
})();