javascriptkeyboard-eventsbookmarkletgoogle-meet

A bookmarklet to customise Google Meet layout


I am using Google Meet for presentations from my Surface tablet. What I do is creating a meeting with the tablet and the desktop PC attached to the projector, and then sharing the tablet screen.

It is time-consuming and boring to setup Google Meet on the desktop PC. Thus, I would like to automate the following tasks with a bookmarklet:

  1. Turn off mic and cam.
  2. Type a guest name.
  3. Click "Ask to join".
  4. Pause for 10 seconds while the tablet accepts the request.
  5. Set the "spotlight" layout and full screen.

Until now, this is my code for Chromium-based browsers.

javascript: (() => {
    document.querySelector('[data-tooltip="Turn off camera (ctrl + e)"]').click();
    document.querySelector('[data-tooltip="Turn off microphone (ctrl + d)"]').click();
    document.querySelector('[placeholder="Your name"]').value = "Desk PC";
    join=document.evaluate("//button[contains(., 'Ask to join')]", document, null,
               XPathResult.ANY_TYPE, null).iterateNext();
    join.disabled = false;
    join.click();
    setTimeout(function() {
    document.querySelector ('button[aria-label="More options"]').click()
    document.evaluate("//span[contains(., 'Change layout')]", document, null,
               XPathResult.ANY_TYPE, null).iterateNext().click();
    document.querySelector ('input[value="Spotlight"]').click()
    }, 10000);        
})();

My essential problem is to send keys to the browser. With actual typing, when I write "Desk PC" in the input element, the event is detected and the join button is activated. If I set the element value with:

document.querySelector('[placeholder="Your name"]').value = "Desk PC";

the join button is not enabled. In theory, I could manually enable and click it:

join.disabled = false;
join.click();

but Meet detects this as a wrong pattern and gives an error. I think, I should simulate sending keystrokes with dispatchEvent() (which would also be used to send an Esc to exit Meet layout menu). However, I am missing the logic of the function. For example, with Stackoverflow page, I expect the following code:

var evt=new KeyboardEvent('keydown', { key: "a" });
input=document.querySelector(".s-input__search");
input.dispatchEvent(evt)

to send an "a" to SO search box, but this is not what happens.

Any help?

Edit

As required in the comments, to reproduce my setting with a single computer:

  1. Type in a Chromium-based browser: https://meet.google.com/new (*).
  2. In the bottom left, copy the resulting meeting room link (perhaps with the copy button).
  3. In a private browser window, navigate to the meeting link (**).
  4. As a guest, you are not required to log in, you just have to type any name to identify yourself.

(*) This simulates the tablet, and you might be required to log in to your account.
(**) This simulates the projecting desk PC, and, because this is typically shared, it's better to use it in private mode and without passwords.


Solution

  • Your problem keyword is actually "trusted input", and when you set value that way (with selector.value), it wouldn't be detected as a trusted input. A workaround as of now's chrome's latest browser is to use document.execCommand (there is no telling when this will stop working).

    I modified your code a little, and I tested it in my own machine on windows 10 + chrome 110:

    javascript: (() => {
        document.querySelector('[data-tooltip="Turn off camera (ctrl + e)"]').click();
        document.querySelector('[data-tooltip="Turn off microphone (ctrl + d)"]').click();
        document.querySelector('[placeholder="Your name"]').focus();
        document.execCommand('insertText', false, "Desk PC");
        const join = document.evaluate("//button[contains(., 'Ask to join')]", document, null, XPathResult.ANY_TYPE, null).iterateNext();
        setTimeout(() => {
            setTimeout(() => {
                setTimeout(() => {
                    document.querySelector('button[aria-label="More options"]').click();
                    setTimeout(() => {
                        document.evaluate("//span[contains(., 'Change layout')]", document, null, XPathResult.ANY_TYPE, null).iterateNext().click();
                        setTimeout(() => {
                            document.querySelector('input[value="Spotlight"]').click();
                        }, 500);
                    }, 500);
                }, 500);
            }, 10000);
            join.click();
        }, 500);
    })();
    

    I know there's a callback hell happening there, but trust me, it has to be that way lol.