Update botplugin

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-21 23:40:43 -03:00
parent 59fa200776
commit 17a3caebab
3 changed files with 3 additions and 132 deletions

View file

@ -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) => {

View file

@ -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);
})();

View file

@ -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 = '<span class="btn-icon">⏳</span> Sending request...';
// Send auth request
chrome.runtime.sendMessage(
{
action: "authenticate",
@ -194,7 +171,6 @@ document.addEventListener("DOMContentLoaded", async function () {
'<span class="btn-icon">📱</span> 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 () {
'<span class="btn-icon">🤖</span> 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();