Add initial implementation of General Bots Chrome extension with settings and UI enhancements
This commit is contained in:
commit
b939138349
14 changed files with 693 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.chrome-debug-profile
|
||||||
|
*.png~
|
33
.vscode/launch.json
vendored
Normal file
33
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "https://web.whatsapp.com",
|
||||||
|
"webRoot": "${workspaceFolder}",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--load-extension=${workspaceFolder}/GeneralBots",
|
||||||
|
"--disable-extensions-except=${workspaceFolder}/GeneralBots",
|
||||||
|
"--user-data-dir=${workspaceFolder}/.chrome-debug-profile"
|
||||||
|
],
|
||||||
|
"sourceMaps": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug Chrome Extension",
|
||||||
|
"runtimeExecutable": "${execPath}",
|
||||||
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}/GeneralBots",
|
||||||
|
"--load-extension=${workspaceFolder}/GeneralBots",
|
||||||
|
"--disable-extensions-except=${workspaceFolder}/GeneralBots"
|
||||||
|
],
|
||||||
|
"outFiles": [
|
||||||
|
"${workspaceFolder}/GeneralBots/**/*.js"
|
||||||
|
],
|
||||||
|
"sourceMaps": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
17
LICENSE
Normal file
17
LICENSE
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
General Bots - WhatsApp Web Enhancement Chrome Extension
|
||||||
|
Copyright (C) 2025 pragmatismo.com.br
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
50
README.md
Normal file
50
README.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# General Bots Chrome Extension
|
||||||
|
|
||||||
|
A professional-grade Chrome extension developed by [pragmatismo.com.br](https://pragmatismo.com.br) that enhances WhatsApp Web with server-side message processing capabilities and UI improvements.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Message Interception: Captures messages before they're sent
|
||||||
|
- Server Processing: Sends message content to your server for processing
|
||||||
|
- Message Replacement: Updates the message with processed content before sending
|
||||||
|
- UI Enhancement: Option to hide the contact list for more chat space
|
||||||
|
- User-friendly Settings: Simple configuration through the extension popup
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Developer Mode Installation
|
||||||
|
|
||||||
|
1. Clone or download this repository
|
||||||
|
2. Open Chrome and navigate to `chrome://extensions/`
|
||||||
|
3. Enable "Developer mode" in the top-right corner
|
||||||
|
4. Click "Load unpacked" and select the extension directory
|
||||||
|
|
||||||
|
### Chrome Web Store Installation
|
||||||
|
|
||||||
|
(Coming soon)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Click the General Bots icon in your Chrome toolbar
|
||||||
|
2. Enter your processing server URL
|
||||||
|
3. Toggle message processing on/off
|
||||||
|
4. Toggle contact list visibility
|
||||||
|
|
||||||
|
## Server API Requirements
|
||||||
|
|
||||||
|
Your server endpoint should:
|
||||||
|
|
||||||
|
1. Accept POST requests with JSON payload: `{ "text": "message content", "timestamp": 1621234567890 }`
|
||||||
|
2. Return JSON response: `{ "processedText": "updated message content" }`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the [GNU Affero General Public License](LICENSE) - see the LICENSE file for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
For support or questions, please contact [pragmatismo.com.br](https://pragmatismo.com.br).
|
18
background.js
Normal file
18
background.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
chrome.runtime.onInstalled.addListener(function() {
|
||||||
|
// Set default settings on installation
|
||||||
|
chrome.storage.sync.set({
|
||||||
|
serverUrl: 'https://api.pragmatismo.com.br/general-bots/process',
|
||||||
|
enableProcessing: true,
|
||||||
|
hideContacts: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for tab updates
|
||||||
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||||
|
if (changeInfo.status === 'complete' && tab.url && tab.url.includes('web.whatsapp.com')) {
|
||||||
|
// Inject content script
|
||||||
|
chrome.tabs.sendMessage(tabId, {
|
||||||
|
action: 'tabReady'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
239
content.js
Normal file
239
content.js
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
// Global variables
|
||||||
|
let settings = {
|
||||||
|
serverUrl: 'https://api.pragmatismo.com.br/general-bots/process',
|
||||||
|
enableProcessing: true,
|
||||||
|
hideContacts: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Original message storage (before processing)
|
||||||
|
const originalMessages = new Map();
|
||||||
|
|
||||||
|
// Initialize the extension
|
||||||
|
function init() {
|
||||||
|
console.log('General Bots: Initializing...');
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
chrome.storage.sync.get({
|
||||||
|
serverUrl: 'https://api.pragmatismo.com.br/general-bots/process',
|
||||||
|
enableProcessing: true,
|
||||||
|
hideContacts: false
|
||||||
|
}, function(items) {
|
||||||
|
settings = items;
|
||||||
|
console.log('General Bots: Settings loaded', settings);
|
||||||
|
|
||||||
|
// Apply hide contacts if enabled
|
||||||
|
applyContactVisibility();
|
||||||
|
|
||||||
|
// Start monitoring the input field
|
||||||
|
setupInputListener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply contact list visibility based on settings
|
||||||
|
function applyContactVisibility() {
|
||||||
|
const contactList = document.querySelector('#pane-side');
|
||||||
|
if (contactList) {
|
||||||
|
if (settings.hideContacts) {
|
||||||
|
contactList.parentElement.classList.add('gb-hide-contacts');
|
||||||
|
} else {
|
||||||
|
contactList.parentElement.classList.remove('gb-hide-contacts');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup input field listener
|
||||||
|
function setupInputListener() {
|
||||||
|
// The main input field where users type messages
|
||||||
|
const inputSelector = 'div[contenteditable="true"][data-tab="10"]';
|
||||||
|
|
||||||
|
// Use MutationObserver to detect when the input field appears
|
||||||
|
const observer = new MutationObserver(mutations => {
|
||||||
|
const inputField = document.querySelector(inputSelector);
|
||||||
|
if (inputField && !inputField.getAttribute('gb-monitored')) {
|
||||||
|
setupFieldMonitoring(inputField);
|
||||||
|
inputField.setAttribute('gb-monitored', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also check immediately in case the field is already present
|
||||||
|
const inputField = document.querySelector(inputSelector);
|
||||||
|
if (inputField && !inputField.getAttribute('gb-monitored')) {
|
||||||
|
setupFieldMonitoring(inputField);
|
||||||
|
inputField.setAttribute('gb-monitored', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup monitoring for a specific input field
|
||||||
|
function setupFieldMonitoring(inputField) {
|
||||||
|
console.log('General Bots: Setting up input field monitoring');
|
||||||
|
|
||||||
|
// Listen for keydown events (Enter key)
|
||||||
|
inputField.addEventListener('keydown', async (event) => {
|
||||||
|
if (event.key === 'Enter' && !event.shiftKey && settings.enableProcessing) {
|
||||||
|
const originalText = inputField.textContent;
|
||||||
|
|
||||||
|
// Only process if there's actual text
|
||||||
|
if (originalText.trim().length > 0) {
|
||||||
|
// Prevent default Enter behavior temporarily
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Process message with server
|
||||||
|
const processedText = await processMessage(originalText);
|
||||||
|
|
||||||
|
// Replace text in the input field
|
||||||
|
inputField.textContent = processedText;
|
||||||
|
|
||||||
|
// Store original message for reference
|
||||||
|
const timestamp = Date.now();
|
||||||
|
originalMessages.set(timestamp, {
|
||||||
|
original: originalText,
|
||||||
|
processed: processedText
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulate Enter press to send the message
|
||||||
|
simulateEnterPress(inputField);
|
||||||
|
|
||||||
|
// Track the message to update it after sending
|
||||||
|
trackSentMessage(timestamp);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('General Bots: Error processing message', error);
|
||||||
|
// Let the original message be sent if there's an error
|
||||||
|
simulateEnterPress(inputField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process message with server
|
||||||
|
async function processMessage(text) {
|
||||||
|
console.log('General Bots: Processing message with server');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(settings.serverUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
text: text,
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server responded with status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.processedText || text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('General Bots: Failed to process message', error);
|
||||||
|
return text; // Return original text if processing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate Enter key press
|
||||||
|
function simulateEnterPress(element) {
|
||||||
|
const enterEvent = new KeyboardEvent('keydown', {
|
||||||
|
key: 'Enter',
|
||||||
|
code: 'Enter',
|
||||||
|
keyCode: 13,
|
||||||
|
which: 13,
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
|
element.dispatchEvent(enterEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track sent message to update it after sending
|
||||||
|
function trackSentMessage(timestamp) {
|
||||||
|
// Monitor for the message to appear in the chat
|
||||||
|
const observer = new MutationObserver(mutations => {
|
||||||
|
// Look for recently sent messages
|
||||||
|
const messageContainers = document.querySelectorAll('.message-out');
|
||||||
|
if (messageContainers.length > 0) {
|
||||||
|
// Get the last sent message
|
||||||
|
const lastMessage = messageContainers[messageContainers.length - 1];
|
||||||
|
|
||||||
|
// Check if this message has our data
|
||||||
|
if (!lastMessage.getAttribute('gb-processed')) {
|
||||||
|
// Get message info
|
||||||
|
const messageInfo = originalMessages.get(timestamp);
|
||||||
|
if (messageInfo) {
|
||||||
|
// Mark as processed
|
||||||
|
lastMessage.setAttribute('gb-processed', timestamp);
|
||||||
|
|
||||||
|
// Update message text if different from original
|
||||||
|
if (messageInfo.original !== messageInfo.processed) {
|
||||||
|
updateSentMessageDisplay(lastMessage, messageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
setTimeout(() => {
|
||||||
|
originalMessages.delete(timestamp);
|
||||||
|
}, 60000); // Remove after 1 minute
|
||||||
|
|
||||||
|
// Stop observing
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start observing chat container
|
||||||
|
const chatContainer = document.querySelector('.copyable-area');
|
||||||
|
if (chatContainer) {
|
||||||
|
observer.observe(chatContainer, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop observing after a reasonable timeout
|
||||||
|
setTimeout(() => {
|
||||||
|
observer.disconnect();
|
||||||
|
}, 10000); // 10 seconds timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the displayed message after sending
|
||||||
|
function updateSentMessageDisplay(messageElement, messageInfo) {
|
||||||
|
const textElement = messageElement.querySelector('.selectable-text');
|
||||||
|
if (textElement) {
|
||||||
|
// Create a small indicator that the message was processed
|
||||||
|
const indicator = document.createElement('div');
|
||||||
|
indicator.className = 'gb-processed-indicator';
|
||||||
|
indicator.title = `Original: "${messageInfo.original}"`;
|
||||||
|
indicator.textContent = '✓ AI processed';
|
||||||
|
|
||||||
|
// Add the indicator to the message
|
||||||
|
messageElement.appendChild(indicator);
|
||||||
|
|
||||||
|
// Add tooltip behavior
|
||||||
|
messageElement.classList.add('gb-processed-message');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for messages from popup or background
|
||||||
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
|
if (message.action === 'settingsUpdated') {
|
||||||
|
settings = message.settings;
|
||||||
|
applyContactVisibility();
|
||||||
|
console.log('General Bots: Settings updated', settings);
|
||||||
|
} else if (message.action === 'tabReady') {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize on load
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
|
||||||
|
// Also try to initialize now in case the page is already loaded
|
||||||
|
init();
|
BIN
icons/icon128.png
Normal file
BIN
icons/icon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 339 B |
BIN
icons/icon16.png
Normal file
BIN
icons/icon16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 339 B |
BIN
icons/icon48.png
Normal file
BIN
icons/icon48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 339 B |
37
manifest.json
Normal file
37
manifest.json
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "General Bots",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Browser server-side processing capabilities",
|
||||||
|
"author": "pragmatismo.com.br",
|
||||||
|
"icons": {
|
||||||
|
"16": "icons/icon16.png",
|
||||||
|
"48": "icons/icon48.png",
|
||||||
|
"128": "icons/icon128.png"
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"storage",
|
||||||
|
"tabs"
|
||||||
|
],
|
||||||
|
"host_permissions": [
|
||||||
|
"https://web.whatsapp.com/*"
|
||||||
|
],
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup.html",
|
||||||
|
"default_icon": {
|
||||||
|
"16": "icons/icon16.png",
|
||||||
|
"48": "icons/icon48.png",
|
||||||
|
"128": "icons/icon128.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["https://web.whatsapp.com/*"],
|
||||||
|
"js": ["content.js"],
|
||||||
|
"css": ["styles.css"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
}
|
||||||
|
}
|
168
popup.css
Normal file
168
popup.css
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 320px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
color: #888;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle span {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 46px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus + .slider {
|
||||||
|
box-shadow: 0 0 1px #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(22px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.round {
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.round:before {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright a {
|
||||||
|
color: #4CAF50;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
47
popup.html
Normal file
47
popup.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>General Bots</title>
|
||||||
|
<link rel="stylesheet" href="popup.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<img src="icons/icon48.png" alt="General Bots Logo">
|
||||||
|
<h1>General Bots</h1>
|
||||||
|
<p class="version">v1.0.0</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings">
|
||||||
|
<div class="setting-item">
|
||||||
|
<label for="server-url">Server URL:</label>
|
||||||
|
<input type="text" id="server-url" placeholder="https://your-server.com/api">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item toggle">
|
||||||
|
<span>Enable Message Processing</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="enable-processing" checked>
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item toggle">
|
||||||
|
<span>Hide Contact Window</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="hide-contacts">
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<button id="save-settings">Save Settings</button>
|
||||||
|
<p class="copyright">© pragmatismo.com.br • <a href="https://github.com/pragmatismo-io/GeneralBots" target="_blank">AGPL License</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="popup.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
50
popup.js
Normal file
50
popup.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Load saved settings
|
||||||
|
chrome.storage.sync.get({
|
||||||
|
serverUrl: 'https://api.pragmatismo.com.br/general-bots/process',
|
||||||
|
enableProcessing: true,
|
||||||
|
hideContacts: false
|
||||||
|
}, function(items) {
|
||||||
|
document.getElementById('server-url').value = items.serverUrl;
|
||||||
|
document.getElementById('enable-processing').checked = items.enableProcessing;
|
||||||
|
document.getElementById('hide-contacts').checked = items.hideContacts;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save settings
|
||||||
|
document.getElementById('save-settings').addEventListener('click', function() {
|
||||||
|
const serverUrl = document.getElementById('server-url').value;
|
||||||
|
const enableProcessing = document.getElementById('enable-processing').checked;
|
||||||
|
const hideContacts = document.getElementById('hide-contacts').checked;
|
||||||
|
|
||||||
|
chrome.storage.sync.set({
|
||||||
|
serverUrl: serverUrl,
|
||||||
|
enableProcessing: enableProcessing,
|
||||||
|
hideContacts: hideContacts
|
||||||
|
}, function() {
|
||||||
|
// Update status to let user know settings were saved
|
||||||
|
const button = document.getElementById('save-settings');
|
||||||
|
const originalText = button.textContent;
|
||||||
|
button.textContent = 'Settings Saved!';
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
// Send message to content script to apply changes immediately
|
||||||
|
chrome.tabs.query({url: 'https://web.whatsapp.com/*'}, function(tabs) {
|
||||||
|
if (tabs.length > 0) {
|
||||||
|
chrome.tabs.sendMessage(tabs[0].id, {
|
||||||
|
action: 'settingsUpdated',
|
||||||
|
settings: {
|
||||||
|
serverUrl: serverUrl,
|
||||||
|
enableProcessing: enableProcessing,
|
||||||
|
hideContacts: hideContacts
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
button.textContent = originalText;
|
||||||
|
button.disabled = false;
|
||||||
|
}, 1500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
32
styles.css
Normal file
32
styles.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/* Hide the contacts pane when enabled */
|
||||||
|
.gb-hide-contacts #pane-side {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand the chat area when contacts are hidden */
|
||||||
|
.gb-hide-contacts #main {
|
||||||
|
width: 100% !important;
|
||||||
|
left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for processed messages */
|
||||||
|
.gb-processed-message {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gb-processed-indicator {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -16px;
|
||||||
|
right: 8px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #53bdeb;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gb-processed-message:hover .gb-processed-indicator {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue