// 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", enableProcessing: true, hideContacts: false, autoMode: false, grammarCorrection: true, whatsappNumber: "", authToken: "", 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", contexts: ["selection"], }); chrome.contextMenus?.create({ id: "gb-translate", title: "Translate with AI", contexts: ["selection"], }); }); // Listen for tab updates chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if ( changeInfo.status === "complete" && tab.url?.includes("web.whatsapp.com") ) { 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 }); // 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); switch (message.action) { case "processText": handleProcessText(message.text, message.options) .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); return true; // Will respond asynchronously case "correctGrammar": handleGrammarCorrection(message.text) .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); return true; case "authenticate": handleAuthentication(message.whatsappNumber) .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); return true; case "getAuthStatus": getAuthStatus() .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); return true; case "generateAutoReply": handleAutoReply(message.context, message.lastMessages) .then(sendResponse) .catch((err) => sendResponse({ error: err.message })); return true; case "getSettings": chrome.storage.sync.get(DEFAULT_CONFIG).then(sendResponse); return true; case "saveSettings": chrome.storage.sync.set(message.settings).then(() => { broadcastSettingsUpdate(message.settings); sendResponse({ success: true }); }); return true; case "showNotification": showNotification(message.title, message.message, message.type); sendResponse({ success: true }); return false; } return false; }); // Context menu click handler chrome.contextMenus?.onClicked.addListener(async (info, tab) => { if (!info.selectionText) return; switch (info.menuItemId) { case "gb-correct-grammar": const corrected = await handleGrammarCorrection(info.selectionText); if (corrected.processedText && tab?.id) { chrome.tabs.sendMessage(tab.id, { action: "replaceSelection", text: corrected.processedText, }); } 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); if (!settings.enableProcessing) { return { processedText: text, changed: false }; } try { const response = await fetch(`${settings.serverUrl}/api/v1/llm/process`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${settings.authToken}`, }, body: JSON.stringify({ text, instanceId: settings.instanceId, options: { grammarCorrection: settings.grammarCorrection, ...options, }, }), }); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } const data = await response.json(); return { processedText: data.processedText || text, changed: data.processedText !== text, corrections: data.corrections || [], }; } 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); try { const response = await fetch(`${settings.serverUrl}/api/v1/llm/grammar`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${settings.authToken}`, }, body: JSON.stringify({ text, instanceId: settings.instanceId, language: "auto", }), }); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } const data = await response.json(); return { processedText: data.correctedText || text, original: text, corrections: data.corrections || [], language: data.detectedLanguage, }; } catch (error) { console.error("General Bots: Grammar correction error", error); return { processedText: text, error: error.message }; } } // Auto-reply generation async function handleAutoReply(context, lastMessages = []) { const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); if (!settings.autoMode) { return { reply: null, autoModeDisabled: true }; } try { const response = await fetch( `${settings.serverUrl}/api/v1/llm/auto-reply`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${settings.authToken}`, }, body: JSON.stringify({ context, lastMessages, instanceId: settings.instanceId, whatsappNumber: settings.whatsappNumber, }), }, ); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } const data = await response.json(); return { reply: data.suggestedReply, confidence: data.confidence, autoSend: data.autoSend && settings.autoMode, }; } catch (error) { console.error("General Bots: Auto-reply error", error); return { reply: null, error: error.message }; } } // 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`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ whatsappNumber, extensionId: chrome.runtime.id, timestamp: Date.now(), }), }, ); if (!response.ok) { throw new Error(`Authentication request failed: ${response.status}`); } 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 }; } catch (error) { console.error("General Bots: Authentication error", error); return { success: false, error: error.message }; } } // 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; } const settings = await chrome.storage.sync.get(DEFAULT_CONFIG); try { const response = await fetch( `${settings.gbServerUrl}/api/v1/auth/whatsapp/status/${requestId}`, ); if (response.ok) { const data = await response.json(); if (data.status === "completed") { await chrome.storage.sync.set({ authToken: data.token, instanceId: data.instanceId, authPending: false, authenticated: true, }); showNotification( "Authentication Complete", "You are now connected to General Bots!", "success", ); // Broadcast to all tabs broadcastSettingsUpdate({ authenticated: true }); return; } else if (data.status === "failed") { await chrome.storage.sync.set({ authPending: false }); showNotification( "Authentication Failed", data.message || "Please try again.", "error", ); return; } } } catch (error) { 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", "authenticated", "whatsappNumber", "instanceId", ]); if (!settings.authToken) { return { authenticated: false }; } // Verify token is still valid try { const response = await fetch( `${DEFAULT_CONFIG.gbServerUrl}/api/v1/auth/verify`, { headers: { Authorization: `Bearer ${settings.authToken}`, }, }, ); if (response.ok) { return { authenticated: true, whatsappNumber: settings.whatsappNumber, instanceId: settings.instanceId, }; } } catch (error) { console.error("General Bots: Verify auth error", error); } // Token invalid, clear auth await chrome.storage.sync.set({ authToken: "", authenticated: false, }); return { authenticated: false }; } // Check for auto-authentication when WhatsApp loads async function checkAutoAuth(tabId) { const settings = await chrome.storage.sync.get([ "authenticated", "autoMode", "whatsappNumber", ]); if (settings.authenticated && settings.autoMode) { // Notify content script that auto mode is active setTimeout(() => { chrome.tabs .sendMessage(tabId, { action: "enableAutoMode", whatsappNumber: settings.whatsappNumber, }) .catch(() => {}); }, 2000); } } // Broadcast settings update to all WhatsApp tabs async function broadcastSettingsUpdate(settings) { const tabs = await chrome.tabs.query({ url: "https://web.whatsapp.com/*" }); for (const tab of tabs) { chrome.tabs .sendMessage(tab.id, { action: "settingsUpdated", settings, }) .catch(() => {}); } } // Show browser notification function showNotification(title, message, type = "info") { const iconPath = type === "error" ? "icons/icon48.png" : "icons/icon48.png"; chrome.notifications?.create({ type: "basic", iconUrl: iconPath, title: `General Bots - ${title}`, message: message, priority: type === "error" ? 2 : 1, }); } // Alarm for periodic tasks chrome.alarms?.create("checkAuth", { periodInMinutes: 30 }); chrome.alarms?.onAlarm.addListener(async (alarm) => { if (alarm.name === "checkAuth") { const status = await getAuthStatus(); if (!status.authenticated) { console.log("General Bots: Auth token expired or invalid"); } } }); console.log("General Bots: Background service worker initialized");