Repository navigation
Expand file tree
/
Copy pathgoogle-meet-code.js
More file actions
66 lines (59 loc) · 3.31 KB
/
Copy pathgoogle-meet-code.js
File metadata and controls
66 lines (59 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// --- SMART-DEDUPLICATION BRIDGE ---
const IGNORE_STRINGS = ["call_end", "Leave call", "More options", "microphone is off", "Turn on microphone", "Turn off microphone", "Meeting details", "People", "Chat", "Raise hand", "Present now", "You are muted", "Captions settings"];
let latestInterviewText = "", lastSentText = "", captionBox = null, retryInterval = null;
function tryFindCaptionBox() {
let box = document.querySelector('[aria-label="Captions"]');
if (!box) {
document.querySelectorAll('[role="region"]').forEach(r => { if (r.getAttribute("aria-label") === "Captions") box = r; });
}
if (!box) box = document.querySelector('div[jsname="YSxPC"]');
return box;
}
function wipeCaptionsFromScreen() { if (captionBox) { captionBox.innerText = ""; console.log("🧹 Visual Clear."); } }
function startBridge() {
console.log("🔍 Searching...");
captionBox = tryFindCaptionBox();
if (!captionBox) { console.log("...Waiting for Captions."); return; }
clearInterval(retryInterval);
console.clear();
console.log("✅ TRIGGER MODE ACTIVE");
captionBox.style.border = "4px solid #00ff00";
const captionObserver = new MutationObserver((mutations) => {
const micBtn = document.querySelector('button[aria-label*="microphone"]');
if (micBtn && micBtn.getAttribute("aria-label").toLowerCase().includes("turn on")) {
let fullText = captionBox.innerText;
if (!fullText) return;
let cleanText = fullText;
if (lastSentText && fullText.startsWith(lastSentText)) cleanText = fullText.substring(lastSentText.length).trim();
if (IGNORE_STRINGS.some(s => cleanText.includes(s))) return;
if (cleanText.length > 5) latestInterviewText = cleanText;
}
});
captionObserver.observe(captionBox, { childList: true, subtree: true, characterData: true });
const micObserver = new MutationObserver((mutations) => {
mutations.forEach((m) => {
if (m.attributeName === "aria-label") {
const label = m.target.getAttribute("aria-label").toLowerCase();
if (label.includes("turn off")) {
console.log("🎤 UNMUTE DETECTED.");
if (latestInterviewText.length > 5) {
sendToPython(latestInterviewText);
lastSentText = captionBox.innerText;
latestInterviewText = "";
wipeCaptionsFromScreen();
}
}
}
});
});
const micBtn = document.querySelector('button[aria-label*="microphone"]');
if (micBtn) micObserver.observe(micBtn, { attributes: true });
}
function sendToPython(text) {
console.log("%c [SENDING]: " + text.substring(0, 50) + "...", "color: blue; font-weight: bold;");
fetch("http://127.0.0.1:5000/caption", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: text }) })
.then(res => res.json())
.then(data => { if (data.reply && data.status !== "ignored") console.log("%c AI REPLY: \n" + data.reply, "background: #000; color: #00ff00; font-size: 16px; padding: 10px; border: 2px solid #00ff00;"); })
.catch(err => console.error("❌ Python Offline"));
}
startBridge(); retryInterval = setInterval(startBridge, 2000);