Auto Read Aloud ChatGPT

This JavaScript snippet is designed to automatically detect and interact with dynamically added “More actions” buttons on a web page and subsequently click the associated “Read aloud” button. It simulates real user interactions by triggering all relevant mouse events, ensuring the UI responds as if a human clicked.

// ==UserScript==
// @name         Auto Read Aloud ChatGPT
// @namespace    http://tampermonkey.net/
// @version      2025-09-14
// @description  This JavaScript snippet is designed to automatically detect and interact with dynamically added “More actions” buttons on a web page and subsequently click the associated “Read aloud” button. It simulates real user interactions by triggering all relevant mouse events, ensuring the UI responds as if a human clicked.
// @author       You
// @match        https://chatgpt.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to simulate a full user click
function simulateFullClick(el) {
  const events = [
    'pointerover',
    'pointerenter',
    'mouseover',
    'mouseenter',
    'mousemove',
    'pointerdown',
    'mousedown',
    'pointerup',
    'mouseup',
    'click'
  ];

  events.forEach(type => {
    const event = new MouseEvent(type, {
      bubbles: true,
      cancelable: true,
      view: window,
      clientX: 0,
      clientY: 0,
      button: 0
    });
    el.dispatchEvent(event);
  });
}

// Function to handle a new "More actions" button
function onNewButton(newButton) {
  console.log('New "More actions" button detected:', newButton);

  // Click the "More actions" button
  simulateFullClick(newButton);

  // Wait for the menu to appear, then click "Read aloud"
  setTimeout(() => {
    const readAloudButton = document.querySelector('[aria-label="Read aloud"]');
    if (readAloudButton) {
      simulateFullClick(readAloudButton);
      console.log('"Read aloud" button clicked!');
    } else {
      console.log('"Read aloud" button not found yet.');
    }
  }, 200); // Adjust delay if menu is slow
}

// Observe the document for new nodes
const observer = new MutationObserver(mutations => {
  mutations.forEach(mutation => {
    mutation.addedNodes.forEach(node => {
      if (node.nodeType === 1) { // element node
        // Check if the added node is a "More actions" button
        if (node.matches('button[aria-label="More actions"]')) {
          onNewButton(node);
        }

        // Also check if any descendants are "More actions" buttons
        node.querySelectorAll?.('button[aria-label="More actions"]').forEach(btn => {
          onNewButton(btn);
        });
      }
    });
  });
});

// Start observing
observer.observe(document.body, { childList: true, subtree: true });
console.log('Watching for new "More actions" buttons...');

})();