Skip to content

Instantly share code, notes, and snippets.

@marcobeltempo
Last active April 12, 2023 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcobeltempo/1e31de4f9cabc52016a7e10ea06e9a28 to your computer and use it in GitHub Desktop.
Save marcobeltempo/1e31de4f9cabc52016a7e10ea06e9a28 to your computer and use it in GitHub Desktop.
A Tampermonkey script to prevent accidentally raising your hand during a Google Meet call
// ==UserScript==
// @name Google Meet Raised Hand Alert
// @namespace https://github.com/marcobeltempo
// @version 0.1
// @description A Tampermonkey script to prevent accidentally raising your hand during a Google Meet call
// @author Marco Beltempo
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// @match https://meet.google.com/*
// @downloadURL https://gist.githubusercontent.com/marcobeltempo/1e31de4f9cabc52016a7e10ea06e9a28/raw/google_meet_raise_hand_alert.js
// @updateURL https://gist.githubusercontent.com/marcobeltempo/1e31de4f9cabc52016a7e10ea06e9a28/raw/google_meet_raise_hand_alert.js
// ==/UserScript==
/**
* KNOWN ISSUES
** pressing "Keep hand raised" disables the alert functionality
** pressing "Keep hand raised" briefly displays a second raise hand button
*/
const RAISE_HAND_LABEL = "Raise hand (ctrl + ⌘ + h)";
const LOWER_HAND_LABEL = "Lower hand (ctrl + ⌘ + h)";
const ALERT_LABEL =
"It sounds like you've said something, so your hand will be lowered.\nKeep it raised";
const CONFIRMATION_MESSAGE = `Click "OK" to raise your hand or click "Cancel" to save yourself the embarrassment`;
let elementRef = null;
let clonedElement = null;
let handIsRaised = false;
let observer = null;
function createObserver(targetNode, cb) {
const config = { attributes: true, childList: true, subtree: true };
// Create an observer instance linked to the callback function
const observer = new MutationObserver(cb);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
return observer;
}
const observerCallback = (mutationList) => {
for (const mutation of mutationList) {
if (
mutation.type === "attributes" &&
mutation.attributeName === "jsaction"
) {
elementRef.style.display = "";
elementRef = null;
handIsRaised = false;
initializeListeners();
}
}
};
function clickOriginalRef() {
elementRef.click();
elementRef.style.display = "";
elementRef = null;
}
function handleClick(event) {
event.preventDefault();
if (handIsRaised) {
clickOriginalRef();
handIsRaised = false;
return initializeListeners();
}
if (confirm(CONFIRMATION_MESSAGE) != true) {
handIsRaised = false;
return;
}
clickOriginalRef();
handIsRaised = true;
// Google meet will automatically lower your hand when you begin talking.
// We set an interval to detect when "<aside>" element popup is visible
let observerInterval = setInterval(() => {
if (observer) {
observer.disconnect();
}
const asideElements = document.getElementsByTagName("aside");
if (asideElements.length === 0) return;
for (const entry of asideElements) {
if (entry.innerText !== ALERT_LABEL) return;
// If the aside element popup is visible, create a MutationObserver
// to detect when it is removed
observer = createObserver(entry, observerCallback);
clearInterval(observerInterval);
}
}, 300);
initializeListeners();
}
function initializeListeners() {
let findRaiseHandButtonInterval = null;
if (!elementRef) {
if (findRaiseHandButtonInterval) {
clearInterval(findRaiseHandButtonInterval);
}
findRaiseHandButtonInterval = setInterval(() => {
elementRef =
document.querySelector(`[aria-label="${RAISE_HAND_LABEL}"]`) ||
document.querySelector(`[aria-label="${LOWER_HAND_LABEL}"]`) ||
null;
if (elementRef) {
initializeListeners();
clearInterval(findRaiseHandButtonInterval);
}
}, 300);
return;
}
const parent = elementRef.parentElement;
if (clonedElement) {
// cleanup the previous clone
clonedElement.removeEventListener("click", handleClick);
clonedElement.remove();
}
clonedElement = elementRef.cloneNode(true);
clonedElement.setAttribute("jsaction", null);
// remove the aria-label from the clone since we rely on it since for identifying the original button
clonedElement.setAttribute("aria-label", "");
clonedElement.addEventListener("click", handleClick);
parent.appendChild(clonedElement);
// hide the original button to maintain the click handlers
elementRef.style.display = "none";
}
(function launch() {
if (window.location.host === "meet.google.com") {
const pathname = window.location.pathname;
const parts = pathname.split("/");
const lastsegment = parts[parts.length - 1];
if (lastsegment.length === 12) {
initializeListeners();
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment