您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automates actions in Facebook Business Manager, specifically Rights Manager.
当前为
- // ==UserScript==
- // @name Facebook Business Automation
- // @namespace http://tampermonkey.net/
- // @version 2.0
- // @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
- // ==/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 scrollToTextareaAndEnterText(containerXPath, textareaXPath, text, maxScrollAttempts = 10) {
- return new Promise((resolve, reject) => {
- const container = getElementByXpath(containerXPath);
- if (!container) {
- return reject(new Error(`Scrollable container not found: ${containerXPath}`));
- }
- let attempts = 0;
- function tryScroll() {
- // Query the textarea directly using the placeholder attribute.
- const textarea = getElementByXpath(textareaXPath);
- if (textarea) {
- console.log(`Found target textarea with XPath: ${textareaXPath}`);
- // 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 XPath: ${textareaXPath}`);
- setTimeout(() => { // ADDED WAIT TIME HERE
- resolve();
- }, 6000)
- } else if (attempts < maxScrollAttempts) {
- container.scrollBy(0, 300); // Scroll down by 300px
- attempts++;
- console.log(`Scrolling attempt ${attempts}...`);
- setTimeout(tryScroll, 500); // Retry after a short delay
- } else {
- reject(new Error(`Target textarea not found after ${maxScrollAttempts} scrolling attempts.`));
- }
- }
- tryScroll();
- });
- }
- // Function to check and click "Confirm my ownership" and handle dispute submission
- // 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[2]/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]";
- 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 disputeTextareaXPath = "/html[1]/body[1]/span[2]/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[1]/div[1]/div[2]/div[1]/div[1]/div[2]/div[1]/div[3]/div[3]/div[1]/div[1]/div[2]/div[1]/div[2]/div[1]/div[1]/div[1]/div[2]/div[1]/div[1]/div[1]/div[1]/div[2]/textarea[1]";
- const disputeText = "Per the supplied composition and CWR Data, West One Music Group is the owner of this sound recording.";
- const submitDisputeXPath = "/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[2]/div/div[2]/div/span/div/div/div";
- const confirmButtonXPath = "/html/body/div[6]/div[1]/div[1]/div/div/div/div/div[3]/div/div[2]/div/span/div/div/div"; // Confirm button after Submit Dispute
- const scrollableContainerXPath = "/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[1]/div[1]";
- function tryConfirmOwnership() {
- waitForElement(actionDivSelector)
- .then(() => {
- const actionDivs = document.querySelectorAll(actionDivSelector);
- for (let i = 0; i < actionDivs.length; i++) {
- if (actionDivs[i].textContent.includes("Confirm my ownership")) {
- actionDivs[i].click();
- console.log("Clicked 'Confirm my ownership'");
- setTimeout(() => { // ADDED WAIT TIME HERE
- // Scroll to textarea, enter text, submit dispute, confirm, then move to next item
- scrollToTextareaAndEnterText(scrollableContainerXPath, disputeTextareaXPath, disputeText)
- .then(() => clickElementByXPath(submitDisputeXPath))
- .then(() => {
- console.log("Added 9-second delay after submitting dispute");
- return new Promise(resolve => setTimeout(resolve, 7000)); // 9-second delay
- })
- // .then(() => waitForElement(() => getElementByXpath(confirmButtonXPath), 15)) // Wait for the confirm button to appear
- .then(() => clickElementByXPath(confirmButtonXPath)) // 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 scrollToTextareaAndEnterText
- console.error("Error during scrollToTextareaAndEnterText or subsequent actions:", error);
- console.log("Retrying from beginning...");
- setTimeout(resumeAutomation, 5000);
- });
- }, 6000)
- return; // Stop looking for "Confirm my ownership" in this iteration
- }
- }
- console.log("Confirm my ownership not available");
- // 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 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";
- const choose_action_xpath = "/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]";
- clickElementByXPath(see_details_xpath)
- .then(() => {
- console.log("Clicked 'See Details', waiting for 'Choose Action'...");
- return waitForElement(() => getElementByXpath(choose_action_xpath), 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 clickElementByXPath(choose_action_xpath);
- })
- .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
- });
- }
- }
- // 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();
- })()