Last active
April 12, 2023 13:08
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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