Facebook Business Automation

Automates actions in Facebook Business Manager, specifically Rights Manager.

当前为 2025-02-14 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Facebook Business Automation
// @namespace    http://tampermonkey.net/
// @version      2.8
// @description  Automates actions in Facebook Business Manager, specifically Rights Manager.
// @author       You
// @match        https://business.facebook.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==

(function () {
    'use strict';

    // Function to get element by XPath
    function getElementByXpath(path) {
        return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }

    // Utility function to wait for an element to appear in the DOM
    function waitForElement(selector, timeoutSeconds = 15) {
        return new Promise((resolve, reject) => {
            const intervalTime = 500; // Check every 500ms
            let elapsedTime = 0;

            const interval = setInterval(() => {
                const element = typeof selector === 'string' ? document.querySelector(selector) : selector();
                if (element) {
                    clearInterval(interval); // Stop checking
                    resolve(element); // Resolve with the found element
                } else if (elapsedTime >= timeoutSeconds * 1000) {
                    clearInterval(interval); // Stop checking after timeout
                    reject(new Error(`Element not found: ${selector}`));
                }
                elapsedTime += intervalTime;
            }, intervalTime);
        });
    }

    // Function to click on an element using XPath and return a promise
    function clickElementByXPath(xpath) {
        return new Promise((resolve, reject) => {
            const element = getElementByXpath(xpath);
            if (element) {
                element.click();
                console.log(`Clicked element with XPath: ${xpath}`);
                resolve(true);
            } else {
                console.log(`Element with XPath not found: ${xpath}`);
                reject(new Error(`Element not found: ${xpath}`));
            }
        });
    }

    // Function to simulate typing into a textarea
    function simulateTyping(element, text) {
        return new Promise(resolve => {
            let index = 0;
            const typingInterval = 50; // Adjust typing speed here (milliseconds per character)

            function typeNextCharacter() {
                if (index < text.length) {
                    const char = text[index];
                    const keyCode = char.charCodeAt(0);

                    // Trigger keydown event
                    const keydownEvent = new KeyboardEvent('keydown', {
                        key: char,
                        code: `Key${char.toUpperCase()}`,
                        which: keyCode,
                        keyCode: keyCode,
                        bubbles: true,
                        cancelable: true
                    });
                    element.dispatchEvent(keydownEvent);

                    // Trigger keypress event
                    const keypressEvent = new KeyboardEvent('keypress', {
                        key: char,
                        code: `Key${char.toUpperCase()}`,
                        which: keyCode,
                        keyCode: keyCode,
                        bubbles: true,
                        cancelable: true
                    });
                    element.dispatchEvent(keypressEvent);

                    // Update the textarea value
                    element.value += char;
                    element.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));

                    // Trigger keyup event
                    const keyupEvent = new KeyboardEvent('keyup', {
                        key: char,
                        code: `Key${char.toUpperCase()}`,
                        which: keyCode,
                        keyCode: keyCode,
                        bubbles: true,
                        cancelable: true
                    });
                    element.dispatchEvent(keyupEvent);

                    index++;
                    setTimeout(typeNextCharacter, typingInterval);
                } else {
                    // All characters have been typed
                    element.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
                    resolve();
                }
            }

            typeNextCharacter();
        });
    }

    // Function to insert text using execCommand
    function insertTextWithExecCommand(textarea, text) {
        textarea.focus();
        try {
            const success = document.execCommand('insertText', false, text);
            if (!success) {
                console.warn("execCommand('insertText') failed, falling back to simulateTyping");
                simulateTyping(textarea, text); // Use simulateTyping as a fallback
            }
        } catch (err) {
            console.error("Error using execCommand, falling back to simulateTyping:", err);
            simulateTyping(textarea, text); // Use simulateTyping as a fallback
        }
        // Dispatch necessary events
        textarea.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
        textarea.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
    }

    // Function to find element by style attribute
    function findElementByStyle(styleAttribute, styleValue) {
        // Get all elements in the document
        const allElements = document.getElementsByTagName('*');

        // Iterate over all elements
        for (let element of allElements) {
            // Check if the element has the specified style attribute and value
            if (element.style[styleAttribute] === styleValue) {
                return element;
            }
        }
        return null;
    }

    function enterTextInTextarea(textareaPlaceholder, text) {
        return new Promise((resolve) => {
            // Query the textarea using the placeholder attribute
            const textarea = document.querySelector(`textarea[placeholder="${textareaPlaceholder}"]`);

            if (textarea) {
                console.log(`Found target textarea with placeholder: ${textareaPlaceholder}`);

                // Clear existing text reliably
                textarea.value = '';
                textarea.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));

                // Insert text using execCommand
                insertTextWithExecCommand(textarea, text);
                console.log(`Entered text into textarea with placeholder: ${textareaPlaceholder}`);
                setTimeout(() => { // ADDED WAIT TIME HERE
                    resolve();
                }, 6000);
            } else {
                console.warn(`Target textarea not found, moving on...`);
                resolve(); // Resolve without error to move on
            }
        });
    }

    // Function to check and click "Confirm my ownership" and handle dispute submission
    function checkAndClickConfirmOwnership() {
        const actionDivSelector = 'div[aria-labelledby][role="menuitem"]';
        const nextItemXPath = "/html[1]/body[1]/span[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[4]/div[1]/div[1]/div[1]/div[2]/div[1]/div[1]/span[1]/div[1]/div[2]/div[1]/div[1]";
        const chooseActionXPath1 = "/html/body/span[2]/div/div[1]/div[1]/div/div/div/div/div/div[3]/div/div[1]/div/div/div/div/div/div/div/div/div[1]/div[2]/div/div/div[2]/div/div/div/div[2]/div/div/div/div[2]/div[1]";
        const chooseActionXPath2 = "/html/body/span/div/div[1]/div[1]/div/div/div/div/div/div[3]/div/div[1]/div/div/div/div/div/div/div/div/div[1]/div[2]/div/div/div[2]/div/div/div/div[2]/div/div/div/div[2]/div[1]/span/div/div/div[1]";
        const disputeTextareaName = 'Add details about your ownership'; // Use the name attribute
        const disputeText = "Per the supplied composition and CWR Data, West One Music Group is the owner of this sound recording.";
        const submitDisputeXPath = "/html[1]/body[1]/span[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[3]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[2]/div[1]/div[2]/div[1]/span[1]/div[1]/div[1]/div[1]";

        function tryConfirmOwnership() {
            waitForElement(actionDivSelector)
                .then(() => {
                    const actionDivs = document.querySelectorAll(actionDivSelector);
                    let ownershipFound = false;

                    for (let i = 0; i < actionDivs.length; i++) {
                        const menuItemText = actionDivs[i].textContent;
                        if (menuItemText.includes("Confirm My Ownership")) {
                            actionDivs[i].click();
                            console.log("Clicked 'Confirm my ownership'");
                            ownershipFound = true;

                            setTimeout(() => { // ADDED WAIT TIME HERE
                                // Enter text, submit dispute, confirm, then move to next item
                                enterTextInTextarea(disputeTextareaName, disputeText)
                                    .then(() => {
                                        console.log("Waiting for 5 seconds before submitting dispute...");
                                        return new Promise(resolve => setTimeout(resolve, 6000)); // 5-second delay
                                    })
                                    .then(() => clickElementByXPath(submitDisputeXPath))
                                    .then(() => {
                                        console.log("Added 9-second delay after submitting dispute");
                                        return new Promise(resolve => setTimeout(resolve, 7000)); // 9-second delay
                                    })
                                    .then(() => clickConfirmButton()) // Click the confirm button
                                    .then(() => {
                                        console.log("Waiting for 5 seconds before submitting dispute...");
                                        return new Promise(resolve => setTimeout(resolve, 6000)); // 5-second delay
                                    })
                                    .then(() => {
                                        console.log("Submitted dispute and confirmed. Moving to next item.");
                                        // Add a 5-second wait here
                                        setTimeout(function () {
                                            // Move to the next item and loop indefinitely
                                            clickElementByXPath(nextItemXPath)
                                                .then(() => clickElementByXPath(chooseActionXPath1))
                                                .catch(() => clickElementByXPath(chooseActionXPath2)) // Fallback to the second XPath
                                                .then(() => {
                                                    console.log("Waiting for 4 seconds before submitting dispute...");
                                                    return new Promise(resolve => setTimeout(resolve, 4000)); // 5-second delay
                                                })
                                                .then(() => tryConfirmOwnership())
                                                .catch(error => {
                                                    console.error("Error moving to next item:", error);
                                                    console.log("Retrying from beginning...");
                                                    setTimeout(resumeAutomation, 5000);
                                                });
                                        }, 5000); // 5000 milliseconds = 5 seconds
                                    })
                                    .catch(error => { // This is the important part - catching the rejection from enterTextInTextarea
                                        console.error("Error during enterTextInTextarea or subsequent actions:", error);
                                        console.log("Ignoring error and moving on...");
                                        // Continue with the next steps regardless of the error
                                        clickElementByXPath(nextItemXPath)
                                            .then(() => clickElementByXPath(chooseActionXPath1))
                                            .catch(() => clickElementByXPath(chooseActionXPath2)) // Fallback to the second XPath
                                            .then(() => {
                                                console.log("Waiting for 4 seconds before submitting dispute...");
                                                return new Promise(resolve => setTimeout(resolve, 4000)); // 5-second delay
                                            })
                                            .then(() => tryConfirmOwnership())
                                            .catch(error => {
                                                console.error("Error moving to next item:", error);
                                                console.log("Retrying from beginning...");
                                                setTimeout(resumeAutomation, 5000);
                                            });
                                    });
                            }, 6000);

                            break; // Exit the loop once "Confirm my ownership" is found and clicked
                        }
                    }

                    if (!ownershipFound) {
                        console.log("Confirm my ownership not available. Moving to the next item.");
                        // Move to the next item if "Confirm my ownership" is not available
                        clickElementByXPath(nextItemXPath)
                            .then(() => {
                                console.log("Waiting for 5 seconds before submitting dispute...");
                                return new Promise(resolve => setTimeout(resolve, 5000)); // 5-second delay
                            })
                            .then(() => clickElementByXPath(chooseActionXPath1))
                            .catch(() => clickElementByXPath(chooseActionXPath2)) // Fallback to the second XPath
                            .then(() => {
                                console.log("Waiting for 5 seconds before submitting dispute...");
                                return new Promise(resolve => setTimeout(resolve, 5000)); // 5-second delay
                            })
                            .then(() => tryConfirmOwnership())
                            .catch(error => {
                                console.error("Error moving to next item:", error);
                            });
                    }
                })
                .catch(error => {
                    console.error("Error in checkAndClickConfirmOwnership:", error);
                });
        }

        tryConfirmOwnership();
    }

    function clickConfirmButton() {
        return new Promise((resolve, reject) => {
            const confirmButton = $("div:contains('Confirm')");
            if (confirmButton.length > 0) {
                confirmButton.click();
                console.log("Clicked confirm button using jQuery");
                resolve(true);
            } else {
                console.log("Confirm button not found using jQuery");
                reject(new Error("Confirm button not found"));
            }
        });
    }

    function navigateAndLog() {
        GM_setValue('automationActive', true);
        window.location.href = 'https://business.facebook.com/latest/rights_manager/rights_manager_action_items?asset_id=114882491608990';
    }

    // Function to wait for a non-empty first row in <tbody>
    function waitForNonEmptyFirstRow(selector, timeoutSeconds = 30) {
        return new Promise((resolve, reject) => {
            const intervalTime = 500; // Check every 500ms
            let elapsedTime = 0;

            const interval = setInterval(() => {
                const firstRow = document.querySelector(selector);
                if (firstRow && firstRow.textContent.trim() !== '') {
                    clearInterval(interval); // Stop checking when non-empty row is found
                    resolve(firstRow);
                } else if (elapsedTime >= timeoutSeconds * 1000) {
                    clearInterval(interval); // Stop checking after timeout
                    reject(new Error(`Non-empty first row not found in: ${selector}`));
                }
                elapsedTime += intervalTime;
            }, intervalTime);
        });
    }

    function resumeAutomation() {
        const automationActive = GM_getValue('automationActive', false);

        if (automationActive) {
            console.log('Resuming automation...');
            waitForNonEmptyFirstRow('tbody tr:first-child', 30)
                .then((firstRow) => {
                    console.log('First item in <tbody>:', firstRow.textContent.trim());

                    setTimeout(() => {
                        const checkbox = firstRow.querySelector('input[type="checkbox"]');
                        if (checkbox) {
                            checkbox.click();
                            console.log('Checkbox clicked!');
                            setTimeout(() => {
                                const see_details_xpath = "/html/body/div[1]/div/div[1]/div/div[2]/div/div/div[1]/span/div/div/div[1]/div[1]/div/div/div/div/div/div/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div[2]/span[2]/div[8]/div[1]/div/div[3]/div[2]/div[1]/div";

                                clickElementByXPath(see_details_xpath)
                                    .then(() => {
                                        console.log("Clicked 'See Details', waiting for 'Choose Action'...");
                                        return waitForElement(() => findChooseActionDiv(), 15);
                                    })
                                    .then(() => {
                                        console.log("Waiting for 5 seconds before submitting dispute...");
                                        return new Promise(resolve => setTimeout(resolve, 5000)); // 5-second delay
                                    })
                                    .then(() => {
                                        console.log("'Choose Action' element found, clicking...");
                                        return findAndClickChooseActionDiv();
                                    })
                                    .then(() => {
                                        console.log("Waiting for 5 seconds before submitting dispute...");
                                        return new Promise(resolve => setTimeout(resolve, 5000)); // 5-second delay
                                    })
                                    .then(() => {
                                        console.log("Clicked 'Choose Action'");
                                        setTimeout(checkAndClickConfirmOwnership, 3000);
                                    })
                                    .then(() => {
                                        console.log("Waiting for 5 seconds before submitting dispute...");
                                        return new Promise(resolve => setTimeout(resolve, 5000)); // 5-second delay
                                    })
                                    .catch(error => {
                                        console.error("Error in automation sequence:", error);
                                    });
                            }, 5000);
                        } else {
                            console.warn('Checkbox not found in the first row.');
                        }

                        GM_setValue('automationActive', false);
                    }, 7000);
                })
                .catch((error) => {
                    console.error(error.message);
                    console.log("Retrying to find a non-empty first row...");
                    resumeAutomation(); // Retry until a non-empty row is found
                });
        }
    }

    function findChooseActionDiv() {
        const divs = document.querySelectorAll('div');
        for (let div of divs) {
            if (div.textContent.trim() === "Choose action") {
                return div;
            }
        }
        return null;
    }

    function findAndClickChooseActionDiv() {
        return new Promise((resolve, reject) => {
            const div = findChooseActionDiv();
            if (div) {
                div.click();
                console.log("Clicked 'Choose action' div");
                resolve();
            } else {
                reject(new Error("'Choose action' div not found"));
            }
        });
    }

    // Add Start button
    const startButton = document.createElement('button');
    startButton.textContent = 'Start';
    startButton.style.position = 'fixed';
    startButton.style.top = '20px';
    startButton.style.left = '20px';
    startButton.style.zIndex = '1000';
    startButton.style.backgroundColor = '#4267B2';
    startButton.style.color = 'white';
    startButton.style.border = 'none';
    startButton.style.padding = '10px 20px';
    startButton.style.cursor = 'pointer';
    startButton.style.borderRadius = '5px';

    startButton.addEventListener('click', navigateAndLog);
    document.body.appendChild(startButton);

    resumeAutomation();
})();