Click buttons across tabs

Clicks specified buttons across tabs using the BroadcastChannel API.

10.06.2025 itibariyledir. En son verisyonu görün.

// ==UserScript==
// @name        Click buttons across tabs
// @namespace   https://musicbrainz.org/user/chaban
// @version     1.0.1
// @tag         ai-created
// @description Clicks specified buttons across tabs using the BroadcastChannel API.
// @author      chaban
// @license     MIT
// @match       *://*.musicbrainz.org/*
// @match       *://magicisrc.kepstin.ca/*
// @match       *://magicisrc-beta.kepstin.ca/*
// @grant       GM.info
// @grant       GM_registerMenuCommand
// ==/UserScript==

(function () {
    'use strict';
    /**
     * @typedef {Object} SiteConfig
     * @property {string|string[]} hostnames - A single hostname string or an array of hostname strings.
     * @property {string|string[]} paths - A single path string or an array of path strings (can be partial matches).
     * @property {string} channelName - The name of the BroadcastChannel to use for this site.
     * @property {string} messageTrigger - The message data that triggers the button click.
     * @property {string} buttonSelector - The CSS selector for the button to be clicked.
     * @property {string} menuCommandName - The name to display in the Tampermonkey/Greasemonkey menu.
     */

    /**
     * Configuration for different websites and their button click settings.
     * @type {SiteConfig[]}
     */
    const siteConfigurations = [
        {
            hostnames: 'musicbrainz.org',
            paths: ['/edit', '/add-cover-art'],
            channelName: 'mb_edit_channel',
            messageTrigger: 'submit-edit',
            buttonSelector: 'button.submit.positive[type="submit"]',
            menuCommandName: 'MusicBrainz: Submit Edit (All Tabs)'
        },

        {
            hostnames: ['magicisrc.kepstin.ca','magicisrc-beta.kepstin.ca'],
            paths: ['/'],
            channelName: 'magicisrc_submit_channel',
            messageTrigger: 'submit-isrcs',
            buttonSelector: '[onclick^="doSubmitISRCs"]',
            menuCommandName: 'MagicISRC: Submit ISRCs (All Tabs)'
        }
    ];

    const scriptName = GM.info.script.name;

    /**
     * Sends a message to the specified BroadcastChannel.
     * @param {string} channelName
     * @param {string} message
     */
    function sendMessageToChannel(channelName, message) {
        try {
            new BroadcastChannel(channelName).postMessage(message);
            console.log(`[${scriptName}] Sent message "${message}" to channel "${channelName}".`);
        } catch (error) {
            console.error(`[${scriptName}] Error sending message to channel "${channelName}":`, error);
        }
    }

    /**
     * Initializes the BroadcastChannel listener for the current site if a match is found.
     * Also registers a menu command if `GM_registerMenuCommand` is available.
     */
    function initializeBroadcastChannelListener() {
        const currentHostname = location.hostname;
        const currentPathname = location.pathname;

        for (const config of siteConfigurations) {
            const hostnames = Array.isArray(config.hostnames) ? config.hostnames : [config.hostnames];
            const paths = Array.isArray(config.paths) ? config.paths : [config.paths];

            const hostnameMatches = hostnames.some(hostname => currentHostname.endsWith(hostname));
            const pathMatches = paths.some(path => currentPathname.endsWith(path));

            if (hostnameMatches && pathMatches) {
                try {
                    const channel = new BroadcastChannel(config.channelName);
                    channel.addEventListener('message', (event) => {
                        if (event.data === config.messageTrigger) {
                            const btn = document.querySelector(config.buttonSelector);
                            if (btn) {
                                btn.click();
                                console.log(`[${scriptName}][${scriptName}] Button clicked for selector "${config.buttonSelector}" on channel "${config.channelName}".`);
                            } else {
                                console.warn(`[${scriptName}] Button with selector "${config.buttonSelector}" not found.`);
                            }
                        }
                    });
                    console.log(`[${scriptName}] Listener active for channel "${config.channelName}".`);
                } catch (error) {
                    console.error(`[${scriptName}] Error initializing BroadcastChannel for "${config.channelName}":`, error);
                }

                if (typeof GM_registerMenuCommand !== 'undefined' && config.menuCommandName) {
                    GM_registerMenuCommand(config.menuCommandName, () => {
                        sendMessageToChannel(config.channelName, config.messageTrigger);
                    });
                    console.log(`[${scriptName}] Registered menu command "${config.menuCommandName}".`);
                } else {
                   console.warn(`[${scriptName}] Couldn't register menu command "${config.menuCommandName}".`)
                }

                // Only activate one listener and register one command per tab for the first matching configuration
                return;
            }
        }
    }

    initializeBroadcastChannelListener();
})();