462 lines
12 KiB
JavaScript
462 lines
12 KiB
JavaScript
|
|
// 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");
|