diff --git a/background.js b/background.js index 9c53644..ea1be09 100644 --- a/background.js +++ b/background.js @@ -1,7 +1,3 @@ -// General Bots - Background Service Worker -// Handles authentication, LLM communication, and message processing - -// Default configuration const DEFAULT_CONFIG = { serverUrl: "https://api.generalbots.com", gbServerUrl: "https://api.pragmatismo.com.br", @@ -14,15 +10,12 @@ const DEFAULT_CONFIG = { instanceId: "", }; -// Initialize extension on install chrome.runtime.onInstalled.addListener(async (details) => { console.log("General Bots: Extension installed/updated", details.reason); - // Set default settings on installation const existing = await chrome.storage.sync.get(DEFAULT_CONFIG); await chrome.storage.sync.set({ ...DEFAULT_CONFIG, ...existing }); - // Create context menu items chrome.contextMenus?.create({ id: "gb-correct-grammar", title: "Correct Grammar with AI", @@ -36,7 +29,6 @@ chrome.runtime.onInstalled.addListener(async (details) => { }); }); -// Listen for tab updates chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if ( changeInfo.status === "complete" && @@ -44,17 +36,12 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { ) { console.log("General Bots: WhatsApp Web detected, initializing..."); - // Notify content script that tab is ready - chrome.tabs.sendMessage(tabId, { action: "tabReady" }).catch(() => { - // Content script may not be loaded yet, that's okay - }); + chrome.tabs.sendMessage(tabId, { action: "tabReady" }).catch(() => {}); - // Check for auto-authentication checkAutoAuth(tabId); } }); -// Handle messages from content script and popup chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.log("General Bots: Received message", message.action); @@ -63,7 +50,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { handleProcessText(message.text, message.options) .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); - return true; // Will respond asynchronously + return true; case "correctGrammar": handleGrammarCorrection(message.text) @@ -109,7 +96,6 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { return false; }); -// Context menu click handler chrome.contextMenus?.onClicked.addListener(async (info, tab) => { if (!info.selectionText) return; @@ -125,12 +111,10 @@ chrome.contextMenus?.onClicked.addListener(async (info, tab) => { break; case "gb-translate": - // Could implement translation here break; } }); -// Process text through LLM async function handleProcessText(text, options = {}) { const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); @@ -167,12 +151,10 @@ async function handleProcessText(text, options = {}) { }; } catch (error) { console.error("General Bots: Process text error", error); - // Fallback - return original text return { processedText: text, changed: false, error: error.message }; } } -// Grammar correction specifically async function handleGrammarCorrection(text) { const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); @@ -207,7 +189,6 @@ async function handleGrammarCorrection(text) { } } -// Auto-reply generation async function handleAutoReply(context, lastMessages = []) { const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); @@ -249,12 +230,10 @@ async function handleAutoReply(context, lastMessages = []) { } } -// Authentication with General Bots via WhatsApp async function handleAuthentication(whatsappNumber) { const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); try { - // Request authentication via General Bots WhatsApp bot const response = await fetch( `${settings.gbServerUrl}/api/v1/auth/whatsapp/request`, { @@ -276,21 +255,18 @@ async function handleAuthentication(whatsappNumber) { const data = await response.json(); - // Save pending auth state await chrome.storage.sync.set({ whatsappNumber, authPending: true, authRequestId: data.requestId, }); - // Show notification to user showNotification( "Authentication Requested", "Check your WhatsApp for a message from General Bots to complete authentication.", "info", ); - // Start polling for auth completion pollAuthCompletion(data.requestId); return { success: true, requestId: data.requestId }; @@ -300,10 +276,8 @@ async function handleAuthentication(whatsappNumber) { } } -// Poll for authentication completion async function pollAuthCompletion(requestId, attempts = 0) { if (attempts > 60) { - // 5 minutes max await chrome.storage.sync.set({ authPending: false }); showNotification("Authentication Timeout", "Please try again.", "error"); return; @@ -333,7 +307,6 @@ async function pollAuthCompletion(requestId, attempts = 0) { "success", ); - // Broadcast to all tabs broadcastSettingsUpdate({ authenticated: true }); return; } else if (data.status === "failed") { @@ -350,11 +323,9 @@ async function pollAuthCompletion(requestId, attempts = 0) { console.error("General Bots: Poll auth error", error); } - // Continue polling setTimeout(() => pollAuthCompletion(requestId, attempts + 1), 5000); } -// Check auth status async function getAuthStatus() { const settings = await chrome.storage.sync.get([ "authToken", @@ -367,7 +338,6 @@ async function getAuthStatus() { return { authenticated: false }; } - // Verify token is still valid try { const response = await fetch( `${DEFAULT_CONFIG.gbServerUrl}/api/v1/auth/verify`, @@ -389,7 +359,6 @@ async function getAuthStatus() { console.error("General Bots: Verify auth error", error); } - // Token invalid, clear auth await chrome.storage.sync.set({ authToken: "", authenticated: false, @@ -398,7 +367,6 @@ async function getAuthStatus() { return { authenticated: false }; } -// Check for auto-authentication when WhatsApp loads async function checkAutoAuth(tabId) { const settings = await chrome.storage.sync.get([ "authenticated", @@ -407,7 +375,6 @@ async function checkAutoAuth(tabId) { ]); if (settings.authenticated && settings.autoMode) { - // Notify content script that auto mode is active setTimeout(() => { chrome.tabs .sendMessage(tabId, { @@ -419,7 +386,6 @@ async function checkAutoAuth(tabId) { } } -// Broadcast settings update to all WhatsApp tabs async function broadcastSettingsUpdate(settings) { const tabs = await chrome.tabs.query({ url: "https://web.whatsapp.com/*" }); @@ -433,7 +399,6 @@ async function broadcastSettingsUpdate(settings) { } } -// Show browser notification function showNotification(title, message, type = "info") { const iconPath = type === "error" ? "icons/icon48.png" : "icons/icon48.png"; @@ -446,7 +411,6 @@ function showNotification(title, message, type = "info") { }); } -// Alarm for periodic tasks chrome.alarms?.create("checkAuth", { periodInMinutes: 30 }); chrome.alarms?.onAlarm.addListener(async (alarm) => { diff --git a/content.js b/content.js index dfd83e3..dc6e307 100644 --- a/content.js +++ b/content.js @@ -1,10 +1,6 @@ -// General Bots - Content Script for WhatsApp Web -// Provides grammar correction, auto-mode, contact hiding, and LLM integration - (function () { "use strict"; - // Global settings let settings = { serverUrl: "https://api.generalbots.com", enableProcessing: true, @@ -15,7 +11,6 @@ authenticated: false, }; - // State management const state = { initialized: false, currentContact: null, @@ -25,7 +20,6 @@ isProcessing: false, }; - // Selectors for WhatsApp Web elements const SELECTORS = { contactList: "#pane-side", chatContainer: ".copyable-area", @@ -40,31 +34,22 @@ searchBox: 'div[data-tab="3"]', }; - // Initialize the extension async function init() { if (state.initialized) return; console.log("General Bots: Initializing content script..."); - // Load settings from storage await loadSettings(); - - // Apply initial UI modifications applyUIModifications(); - - // Setup observers and listeners setupInputListener(); setupMessageObserver(); setupContactObserver(); - - // Inject custom UI elements injectControlPanel(); state.initialized = true; console.log("General Bots: Content script initialized"); } - // Load settings from chrome storage async function loadSettings() { return new Promise((resolve) => { chrome.storage.sync.get(settings, (items) => { @@ -75,13 +60,11 @@ }); } - // Apply UI modifications based on settings function applyUIModifications() { applyContactVisibility(); applyAutoModeIndicator(); } - // Hide/show contact list function applyContactVisibility() { const contactList = document.querySelector(SELECTORS.contactList); if (contactList) { @@ -97,7 +80,6 @@ } } - // Show auto-mode indicator function applyAutoModeIndicator() { let indicator = document.getElementById("gb-auto-mode-indicator"); @@ -118,7 +100,6 @@ } } - // Setup input field listener for message processing function setupInputListener() { const observer = new MutationObserver(() => { const inputField = document.querySelector(SELECTORS.messageInput); @@ -133,7 +114,6 @@ subtree: true, }); - // Check immediately const inputField = document.querySelector(SELECTORS.messageInput); if (inputField && !inputField.getAttribute("gb-monitored")) { setupFieldMonitoring(inputField); @@ -141,11 +121,9 @@ } } - // Monitor input field for messages function setupFieldMonitoring(inputField) { console.log("General Bots: Setting up input field monitoring"); - // Listen for keydown events inputField.addEventListener("keydown", async (event) => { if (event.key === "Enter" && !event.shiftKey) { const originalText = inputField.textContent.trim(); @@ -163,7 +141,6 @@ result.processedText && result.processedText !== originalText ) { - // Show correction preview const shouldSend = await showCorrectionPreview( originalText, result.processedText, @@ -172,7 +149,6 @@ if (shouldSend) { setInputText(inputField, result.processedText); - // Store original for reference state.originalMessages.set(Date.now(), { original: originalText, corrected: result.processedText, @@ -181,7 +157,6 @@ } hideProcessingIndicator(); - // Send the message simulateEnterPress(inputField); } catch (error) { console.error("General Bots: Error processing message", error); @@ -193,11 +168,9 @@ } }); - // Add visual indicator that field is being monitored inputField.classList.add("gb-monitored-input"); } - // Process message through LLM for grammar correction async function processMessageWithLLM(text) { return new Promise((resolve) => { chrome.runtime.sendMessage( @@ -220,10 +193,8 @@ }); } - // Show correction preview modal async function showCorrectionPreview(original, corrected, inputField) { return new Promise((resolve) => { - // If texts are very similar, auto-accept if (levenshteinDistance(original, corrected) < 3) { resolve(true); return; @@ -258,7 +229,6 @@ document.body.appendChild(modal); - // Auto-close after 5 seconds with corrected text const autoClose = setTimeout(() => { modal.remove(); resolve(true); @@ -278,7 +248,6 @@ }); } - // Setup observer for incoming messages (for auto-reply) function setupMessageObserver() { const observer = new MutationObserver((mutations) => { if (!settings.autoMode) return; @@ -300,7 +269,6 @@ } }); - // Start observing when chat container is available const waitForChat = setInterval(() => { const chatContainer = document.querySelector(SELECTORS.chatContainer); if (chatContainer) { @@ -314,14 +282,12 @@ }, 1000); } - // Handle incoming message for auto-reply async function handleIncomingMessage(messageElement) { if (!settings.autoMode || !settings.authenticated) return; const currentContact = getCurrentContactName(); if (!currentContact) return; - // Check if auto-mode is enabled for this contact if (!state.autoModeContacts.has(currentContact)) return; const messageText = messageElement.querySelector(SELECTORS.messageText); @@ -335,10 +301,8 @@ text, ); - // Get conversation context const context = getConversationContext(); - // Request auto-reply from LLM chrome.runtime.sendMessage( { action: "generateAutoReply", @@ -356,7 +320,6 @@ ); } - // Get conversation context (last few messages) function getConversationContext() { const messages = []; const messageElements = document.querySelectorAll( @@ -378,20 +341,17 @@ return messages; } - // Send auto-reply async function sendAutoReply(text) { const inputField = document.querySelector(SELECTORS.messageInput); if (!inputField) return; setInputText(inputField, text); - // Small delay before sending await new Promise((r) => setTimeout(r, 500)); simulateEnterPress(inputField); } - // Setup contact observer for auto-mode toggle per contact function setupContactObserver() { const observer = new MutationObserver(() => { const header = document.querySelector(SELECTORS.chatHeader); @@ -406,7 +366,6 @@ }); } - // Inject control buttons for current contact function injectContactControls(header) { const contactName = getCurrentContactName(); if (!contactName) return; @@ -442,7 +401,6 @@ }); } - // Inject main control panel function injectControlPanel() { const panel = document.createElement("div"); panel.id = "gb-control-panel"; @@ -494,14 +452,10 @@ `; document.body.appendChild(panel); - - // Setup panel event listeners setupPanelListeners(); } - // Setup control panel event listeners function setupPanelListeners() { - // Panel toggle document .getElementById("gb-panel-toggle") ?.addEventListener("click", function () { @@ -515,7 +469,6 @@ } }); - // Grammar toggle document .getElementById("gb-grammar-toggle") ?.addEventListener("change", function () { @@ -523,7 +476,6 @@ saveSettings(); }); - // Contacts toggle document .getElementById("gb-contacts-toggle") ?.addEventListener("change", function () { @@ -532,7 +484,6 @@ saveSettings(); }); - // Auto mode toggle document .getElementById("gb-auto-toggle") ?.addEventListener("change", function () { @@ -541,7 +492,6 @@ saveSettings(); }); - // Auth button document .getElementById("gb-auth-btn") ?.addEventListener("click", async function () { @@ -576,7 +526,6 @@ }); } - // Save settings to storage function saveSettings() { chrome.storage.sync.set(settings); chrome.runtime.sendMessage({ @@ -585,19 +534,16 @@ }); } - // Get current contact name function getCurrentContactName() { const nameEl = document.querySelector(SELECTORS.contactName); return nameEl ? nameEl.textContent.trim() : null; } - // Set text in input field function setInputText(inputField, text) { inputField.textContent = text; inputField.dispatchEvent(new InputEvent("input", { bubbles: true })); } - // Simulate Enter key press function simulateEnterPress(element) { const enterEvent = new KeyboardEvent("keydown", { key: "Enter", @@ -610,7 +556,6 @@ element.dispatchEvent(enterEvent); } - // Show processing indicator function showProcessingIndicator(inputField) { let indicator = document.getElementById("gb-processing"); if (!indicator) { @@ -626,7 +571,6 @@ indicator.style.display = "flex"; } - // Hide processing indicator function hideProcessingIndicator() { const indicator = document.getElementById("gb-processing"); if (indicator) { @@ -634,14 +578,12 @@ } } - // Utility: Escape HTML function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } - // Utility: Levenshtein distance for text comparison function levenshteinDistance(str1, str2) { const m = str1.length; const n = str2.length; @@ -665,7 +607,6 @@ return dp[m][n]; } - // Listen for messages from background script chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (message.action) { case "tabReady": @@ -694,7 +635,6 @@ case "authCompleted": settings.authenticated = true; - // Refresh control panel const panel = document.getElementById("gb-control-panel"); if (panel) { panel.remove(); @@ -707,13 +647,11 @@ return true; }); - // Initialize on DOM ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } - // Also try to initialize after a delay (WhatsApp Web loads dynamically) setTimeout(init, 2000); })(); diff --git a/popup.js b/popup.js index a035d47..8b1ca11 100644 --- a/popup.js +++ b/popup.js @@ -1,8 +1,4 @@ -// General Bots - Popup Script -// Handles settings management, authentication, and UI updates - document.addEventListener("DOMContentLoaded", async function () { - // Default settings const DEFAULT_SETTINGS = { serverUrl: "https://api.generalbots.com", gbServerUrl: "https://api.pragmatismo.com.br", @@ -21,19 +17,15 @@ document.addEventListener("DOMContentLoaded", async function () { }, }; - // Load settings and update UI await loadSettings(); await checkAuthStatus(); loadStats(); - // Event listeners setupEventListeners(); - // Load saved settings async function loadSettings() { return new Promise((resolve) => { chrome.storage.sync.get(DEFAULT_SETTINGS, function (items) { - // Update form fields document.getElementById("server-url").value = items.serverUrl || DEFAULT_SETTINGS.serverUrl; document.getElementById("whatsapp-number").value = @@ -50,7 +42,6 @@ document.addEventListener("DOMContentLoaded", async function () { }); } - // Check authentication status async function checkAuthStatus() { return new Promise((resolve) => { chrome.runtime.sendMessage({ action: "getAuthStatus" }, (response) => { @@ -78,7 +69,6 @@ document.addEventListener("DOMContentLoaded", async function () { }); } - // Load statistics function loadStats() { chrome.storage.local.get(["stats"], function (result) { const stats = result.stats || DEFAULT_SETTINGS.stats; @@ -91,24 +81,19 @@ document.addEventListener("DOMContentLoaded", async function () { }); } - // Setup event listeners function setupEventListeners() { - // Save settings button document .getElementById("save-settings") .addEventListener("click", saveSettings); - // Auth button document .getElementById("auth-btn") .addEventListener("click", handleAuthentication); - // Open options page document.getElementById("open-options").addEventListener("click", () => { chrome.runtime.openOptionsPage(); }); - // Toggle switches - save immediately on change const toggles = [ "grammar-correction", "enable-processing", @@ -117,12 +102,11 @@ document.addEventListener("DOMContentLoaded", async function () { ]; toggles.forEach((id) => { document.getElementById(id).addEventListener("change", function () { - saveSettings(true); // silent save + saveSettings(true); }); }); } - // Save settings async function saveSettings(silent = false) { const settings = { serverUrl: document.getElementById("server-url").value.trim(), @@ -135,7 +119,6 @@ document.addEventListener("DOMContentLoaded", async function () { return new Promise((resolve) => { chrome.storage.sync.set(settings, function () { - // Notify content script about settings change chrome.tabs.query( { url: "https://web.whatsapp.com/*" }, function (tabs) { @@ -157,32 +140,26 @@ document.addEventListener("DOMContentLoaded", async function () { }); } - // Handle authentication async function handleAuthentication() { const numberInput = document.getElementById("whatsapp-number"); const authBtn = document.getElementById("auth-btn"); const number = numberInput.value.trim(); - // Validate number if (!number) { showError(numberInput, "Please enter your WhatsApp number"); return; } - // Clean number (remove spaces, dashes) const cleanNumber = number.replace(/[\s\-\(\)]/g, ""); - // Basic validation if (!/^\+?[0-9]{10,15}$/.test(cleanNumber)) { showError(numberInput, "Invalid phone number format"); return; } - // Update button state authBtn.disabled = true; authBtn.innerHTML = ' Sending request...'; - // Send auth request chrome.runtime.sendMessage( { action: "authenticate", @@ -194,7 +171,6 @@ document.addEventListener("DOMContentLoaded", async function () { '📱 Check your WhatsApp'; authBtn.classList.add("btn-success"); - // Start polling for auth completion pollAuthStatus(); } else { authBtn.disabled = false; @@ -208,10 +184,8 @@ document.addEventListener("DOMContentLoaded", async function () { ); } - // Poll for authentication status function pollAuthStatus(attempts = 0) { if (attempts > 60) { - // 5 minutes max showNotification("Authentication timed out. Please try again.", "error"); resetAuthButton(); return; @@ -229,7 +203,6 @@ document.addEventListener("DOMContentLoaded", async function () { }, 5000); } - // Reset auth button function resetAuthButton() { const authBtn = document.getElementById("auth-btn"); authBtn.disabled = false; @@ -238,7 +211,6 @@ document.addEventListener("DOMContentLoaded", async function () { '🤖 Authenticate via WhatsApp'; } - // Show error on input function showError(input, message) { input.classList.add("error"); const small = input.parentElement.querySelector("small"); @@ -256,7 +228,6 @@ document.addEventListener("DOMContentLoaded", async function () { }, 3000); } - // Show feedback on button function showFeedback(buttonId, message, type = "success") { const button = document.getElementById(buttonId); const originalHTML = button.innerHTML; @@ -273,9 +244,7 @@ document.addEventListener("DOMContentLoaded", async function () { }, 1500); } - // Show notification function showNotification(message, type = "info") { - // Remove existing notification const existing = document.querySelector(".popup-notification"); if (existing) existing.remove();