(function () { "use strict"; let settings = { serverUrl: "https://api.generalbots.com", enableProcessing: true, hideContacts: false, autoMode: false, grammarCorrection: true, whatsappNumber: "", authenticated: false, }; const state = { initialized: false, currentContact: null, autoModeContacts: new Set(), originalMessages: new Map(), processingQueue: [], isProcessing: false, }; const SELECTORS = { contactList: "#pane-side", chatContainer: ".copyable-area", messageInput: 'div[contenteditable="true"][data-tab="10"]', sendButton: 'button[data-tab="11"]', messageOut: ".message-out", messageIn: ".message-in", messageText: ".selectable-text", contactName: "header ._amig span", chatHeader: "header._amid", conversationPanel: "#main", searchBox: 'div[data-tab="3"]', }; async function init() { if (state.initialized) return; console.log("General Bots: Initializing content script..."); await loadSettings(); applyUIModifications(); setupInputListener(); setupMessageObserver(); setupContactObserver(); injectControlPanel(); state.initialized = true; console.log("General Bots: Content script initialized"); } async function loadSettings() { return new Promise((resolve) => { chrome.storage.sync.get(settings, (items) => { settings = { ...settings, ...items }; console.log("General Bots: Settings loaded", settings); resolve(settings); }); }); } function applyUIModifications() { applyContactVisibility(); applyAutoModeIndicator(); } function applyContactVisibility() { const contactList = document.querySelector(SELECTORS.contactList); if (contactList) { const parent = contactList.parentElement; if (settings.hideContacts) { parent.classList.add("gb-hide-contacts"); parent.style.cssText = "width: 0 !important; min-width: 0 !important; overflow: hidden !important;"; } else { parent.classList.remove("gb-hide-contacts"); parent.style.cssText = ""; } } } function applyAutoModeIndicator() { let indicator = document.getElementById("gb-auto-mode-indicator"); if (settings.autoMode) { if (!indicator) { indicator = document.createElement("div"); indicator.id = "gb-auto-mode-indicator"; indicator.className = "gb-auto-indicator"; indicator.innerHTML = ` Auto Mode Active `; document.body.appendChild(indicator); } indicator.style.display = "flex"; } else if (indicator) { indicator.style.display = "none"; } } function setupInputListener() { const observer = new MutationObserver(() => { const inputField = document.querySelector(SELECTORS.messageInput); if (inputField && !inputField.getAttribute("gb-monitored")) { setupFieldMonitoring(inputField); inputField.setAttribute("gb-monitored", "true"); } }); observer.observe(document.body, { childList: true, subtree: true, }); const inputField = document.querySelector(SELECTORS.messageInput); if (inputField && !inputField.getAttribute("gb-monitored")) { setupFieldMonitoring(inputField); inputField.setAttribute("gb-monitored", "true"); } } function setupFieldMonitoring(inputField) { console.log("General Bots: Setting up input field monitoring"); inputField.addEventListener("keydown", async (event) => { if (event.key === "Enter" && !event.shiftKey) { const originalText = inputField.textContent.trim(); if (originalText.length > 0 && settings.enableProcessing) { if (settings.grammarCorrection) { event.preventDefault(); event.stopPropagation(); try { showProcessingIndicator(inputField); const result = await processMessageWithLLM(originalText); if ( result.processedText && result.processedText !== originalText ) { const shouldSend = await showCorrectionPreview( originalText, result.processedText, inputField, ); if (shouldSend) { setInputText(inputField, result.processedText); state.originalMessages.set(Date.now(), { original: originalText, corrected: result.processedText, }); } } hideProcessingIndicator(); simulateEnterPress(inputField); } catch (error) { console.error("General Bots: Error processing message", error); hideProcessingIndicator(); simulateEnterPress(inputField); } } } } }); inputField.classList.add("gb-monitored-input"); } async function processMessageWithLLM(text) { return new Promise((resolve) => { chrome.runtime.sendMessage( { action: "correctGrammar", text: text, }, (response) => { if (chrome.runtime.lastError) { console.error( "General Bots: Runtime error", chrome.runtime.lastError, ); resolve({ processedText: text }); return; } resolve(response || { processedText: text }); }, ); }); } async function showCorrectionPreview(original, corrected, inputField) { return new Promise((resolve) => { if (levenshteinDistance(original, corrected) < 3) { resolve(true); return; } const modal = document.createElement("div"); modal.className = "gb-correction-modal"; modal.innerHTML = `
Grammar Correction

${escapeHtml(original)}

${escapeHtml(corrected)}

`; document.body.appendChild(modal); const autoClose = setTimeout(() => { modal.remove(); resolve(true); }, 5000); document.getElementById("gb-accept").addEventListener("click", () => { clearTimeout(autoClose); modal.remove(); resolve(true); }); document.getElementById("gb-reject").addEventListener("click", () => { clearTimeout(autoClose); modal.remove(); resolve(false); }); }); } function setupMessageObserver() { const observer = new MutationObserver((mutations) => { if (!settings.autoMode) return; for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { const incomingMsg = node.querySelector ? node.querySelector(SELECTORS.messageIn) : null; if ( incomingMsg || (node.classList && node.classList.contains("message-in")) ) { handleIncomingMessage(incomingMsg || node); } } } } }); const waitForChat = setInterval(() => { const chatContainer = document.querySelector(SELECTORS.chatContainer); if (chatContainer) { clearInterval(waitForChat); observer.observe(chatContainer, { childList: true, subtree: true, }); console.log("General Bots: Message observer started"); } }, 1000); } async function handleIncomingMessage(messageElement) { if (!settings.autoMode || !settings.authenticated) return; const currentContact = getCurrentContactName(); if (!currentContact) return; if (!state.autoModeContacts.has(currentContact)) return; const messageText = messageElement.querySelector(SELECTORS.messageText); if (!messageText) return; const text = messageText.textContent.trim(); if (!text) return; console.log( "General Bots: Processing incoming message for auto-reply", text, ); const context = getConversationContext(); chrome.runtime.sendMessage( { action: "generateAutoReply", context: { contact: currentContact, lastMessage: text, }, lastMessages: context, }, async (response) => { if (response && response.reply && response.autoSend) { await sendAutoReply(response.reply); } }, ); } function getConversationContext() { const messages = []; const messageElements = document.querySelectorAll( `${SELECTORS.messageIn}, ${SELECTORS.messageOut}`, ); const recentMessages = Array.from(messageElements).slice(-10); for (const msg of recentMessages) { const textEl = msg.querySelector(SELECTORS.messageText); if (textEl) { messages.push({ type: msg.classList.contains("message-out") ? "sent" : "received", text: textEl.textContent.trim(), }); } } return messages; } async function sendAutoReply(text) { const inputField = document.querySelector(SELECTORS.messageInput); if (!inputField) return; setInputText(inputField, text); await new Promise((r) => setTimeout(r, 500)); simulateEnterPress(inputField); } function setupContactObserver() { const observer = new MutationObserver(() => { const header = document.querySelector(SELECTORS.chatHeader); if (header && !header.querySelector(".gb-contact-controls")) { injectContactControls(header); } }); observer.observe(document.body, { childList: true, subtree: true, }); } function injectContactControls(header) { const contactName = getCurrentContactName(); if (!contactName) return; const controls = document.createElement("div"); controls.className = "gb-contact-controls"; const isAutoEnabled = state.autoModeContacts.has(contactName); controls.innerHTML = ` `; header.appendChild(controls); document .getElementById("gb-toggle-auto") .addEventListener("click", function () { if (state.autoModeContacts.has(contactName)) { state.autoModeContacts.delete(contactName); this.classList.remove("active"); this.querySelector(".gb-label").textContent = "Auto OFF"; } else { state.autoModeContacts.add(contactName); this.classList.add("active"); this.querySelector(".gb-label").textContent = "Auto ON"; } }); } function injectControlPanel() { const panel = document.createElement("div"); panel.id = "gb-control-panel"; panel.className = "gb-panel"; panel.innerHTML = `
General Bots
${settings.authenticated ? "Connected" : "Not Connected"}
${ !settings.authenticated ? `

Connect with your General Bots account:

` : "" }
`; document.body.appendChild(panel); setupPanelListeners(); } function setupPanelListeners() { document .getElementById("gb-panel-toggle") ?.addEventListener("click", function () { const body = document.getElementById("gb-panel-body"); if (body.style.display === "none") { body.style.display = "block"; this.textContent = "−"; } else { body.style.display = "none"; this.textContent = "+"; } }); document .getElementById("gb-grammar-toggle") ?.addEventListener("change", function () { settings.grammarCorrection = this.checked; saveSettings(); }); document .getElementById("gb-contacts-toggle") ?.addEventListener("change", function () { settings.hideContacts = this.checked; applyContactVisibility(); saveSettings(); }); document .getElementById("gb-auto-toggle") ?.addEventListener("change", function () { settings.autoMode = this.checked; applyAutoModeIndicator(); saveSettings(); }); document .getElementById("gb-auth-btn") ?.addEventListener("click", async function () { const numberInput = document.getElementById("gb-whatsapp-number"); const number = numberInput?.value.trim(); if (!number) { alert("Please enter your WhatsApp number"); return; } this.disabled = true; this.textContent = "Authenticating..."; chrome.runtime.sendMessage( { action: "authenticate", whatsappNumber: number, }, (response) => { if (response.success) { this.textContent = "Check WhatsApp"; } else { this.textContent = "Authenticate"; this.disabled = false; alert( "Authentication failed: " + (response.error || "Unknown error"), ); } }, ); }); } function saveSettings() { chrome.storage.sync.set(settings); chrome.runtime.sendMessage({ action: "saveSettings", settings: settings, }); } function getCurrentContactName() { const nameEl = document.querySelector(SELECTORS.contactName); return nameEl ? nameEl.textContent.trim() : null; } function setInputText(inputField, text) { inputField.textContent = text; inputField.dispatchEvent(new InputEvent("input", { bubbles: true })); } function simulateEnterPress(element) { const enterEvent = new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true, }); element.dispatchEvent(enterEvent); } function showProcessingIndicator(inputField) { let indicator = document.getElementById("gb-processing"); if (!indicator) { indicator = document.createElement("div"); indicator.id = "gb-processing"; indicator.className = "gb-processing-indicator"; indicator.innerHTML = `
Processing with AI... `; inputField.parentElement.appendChild(indicator); } indicator.style.display = "flex"; } function hideProcessingIndicator() { const indicator = document.getElementById("gb-processing"); if (indicator) { indicator.style.display = "none"; } } function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } function levenshteinDistance(str1, str2) { const m = str1.length; const n = str2.length; const dp = Array(m + 1) .fill(null) .map(() => Array(n + 1).fill(0)); for (let i = 0; i <= m; i++) dp[i][0] = i; for (let j = 0; j <= n; j++) dp[0][j] = j; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (str1[i - 1] === str2[j - 1]) { dp[i][j] = dp[i - 1][j - 1]; } else { dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1; } } } return dp[m][n]; } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (message.action) { case "tabReady": init(); break; case "settingsUpdated": settings = { ...settings, ...message.settings }; applyUIModifications(); break; case "enableAutoMode": settings.autoMode = true; settings.whatsappNumber = message.whatsappNumber; applyAutoModeIndicator(); break; case "replaceSelection": const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); range.deleteContents(); range.insertNode(document.createTextNode(message.text)); } break; case "authCompleted": settings.authenticated = true; const panel = document.getElementById("gb-control-panel"); if (panel) { panel.remove(); injectControlPanel(); } break; } sendResponse({ success: true }); return true; }); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } setTimeout(init, 2000); })();