agentic-os/whatsapp-gateway/public/app.js

252 lines
8.5 KiB
JavaScript

const socket = io({ path: '/proxy/whatsapp/socket.io' });
// UI Elements
const statusBadge = document.getElementById('status-badge');
const qrContainer = document.getElementById('qr-container');
const activityLog = document.getElementById('activity-log');
const groupsTableBody = document.getElementById('groups-table-body');
const appsTableBody = document.getElementById('apps-table-body');
const inviteModal = document.getElementById('invite-modal');
const inviteLinkInput = document.getElementById('invite-link-input');
// Tab Switching
document.querySelectorAll('.nav-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
e.target.classList.add('active');
document.getElementById(e.target.dataset.tab).classList.add('active');
if (e.target.dataset.tab === 'groups') {
fetchGroups();
}
if (e.target.dataset.tab === 'apps') {
fetchApps();
}
});
});
// Logging
function logActivity(msg, type = 'info') {
const entry = document.createElement('p');
entry.className = `log-entry ${type}`;
const time = new Date().toLocaleTimeString();
entry.textContent = `[${time}] ${msg}`;
activityLog.prepend(entry);
}
// Socket Events
socket.on('status', (data) => {
updateStatus(data.ready);
if (data.qr && !data.ready) renderQR(data.qr);
});
socket.on('qr', (qr) => {
renderQR(qr);
logActivity('New QR code generated', 'system');
});
socket.on('ready', () => {
updateStatus(true);
qrContainer.innerHTML = '<h3>✅ Connected</h3><p>Session is active and authenticated.</p>';
logActivity('Client successfully authenticated and ready', 'success');
});
socket.on('disconnected', (reason) => {
updateStatus(false);
logActivity(`Disconnected: ${reason}`, 'error');
});
socket.on('incoming_message', (msg) => {
logActivity(`Msg from ${msg.from}: ${msg.body.substring(0, 50)}...`, 'info');
});
function updateStatus(isReady) {
if (isReady) {
statusBadge.textContent = 'Connected';
statusBadge.className = 'badge success';
} else {
statusBadge.textContent = 'Disconnected';
statusBadge.className = 'badge error';
}
}
function renderQR(qrText) {
qrContainer.innerHTML = '';
const qr = qrcode(0, 'L');
qr.addData(qrText);
qr.make();
qrContainer.innerHTML = qr.createImgTag(5);
}
// Groups Tab functionality
async function fetchGroups() {
groupsTableBody.innerHTML = '<tr><td colspan="3" class="text-center">Loading...</td></tr>';
try {
const res = await fetch('api/groups');
const data = await res.json();
if (!data.success) {
groupsTableBody.innerHTML = `<tr><td colspan="3" class="text-center">Error: ${data.error || 'Not connected'}</td></tr>`;
return;
}
if (data.groups.length === 0) {
groupsTableBody.innerHTML = '<tr><td colspan="3" class="text-center">No groups found.</td></tr>';
return;
}
const settingsRes = await fetch('api/settings');
const settingsData = await settingsRes.json();
const defaultDest = settingsData.settings?.defaultDestination || '';
groupsTableBody.innerHTML = '';
data.groups.forEach(g => {
const isDefault = g.id === defaultDest;
const tr = document.createElement('tr');
tr.innerHTML = `
<td>
<strong>${g.name}</strong>
${isDefault ? '<span class="badge success" style="margin-left:8px;font-size:10px;">Default</span>' : ''}
<br><small style="color:var(--text-muted)">${g.id}</small>
</td>
<td>${g.participants}</td>
<td>
${!isDefault ? `<button class="primary-btn" onclick="setDefaultDestination('${g.id}')" style="margin-right: 8px;">Set Default</button>` : ''}
</td>
`;
groupsTableBody.appendChild(tr);
});
} catch (e) {
groupsTableBody.innerHTML = `<tr><td colspan="3" class="text-center">Fetch failed.</td></tr>`;
}
}
async function setDefaultDestination(groupId) {
try {
const res = await fetch('api/settings/default-destination', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ destinationId: groupId })
});
const data = await res.json();
if (data.success) {
fetchGroups();
} else {
alert('Error setting default: ' + data.error);
}
} catch (e) {
alert('Failed to set default destination.');
}
}
document.getElementById('add-group-link-form').addEventListener('submit', async (e) => {
e.preventDefault();
const btn = e.target.querySelector('button');
const input = document.getElementById('new-invite-link');
btn.disabled = true;
btn.textContent = 'Adding...';
try {
const res = await fetch('api/groups/add-by-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ link: input.value })
});
const data = await res.json();
if (data.success) {
input.value = '';
fetchGroups();
} else {
alert('Error adding group: ' + data.error);
}
} catch (err) {
alert('Failed to add group');
}
btn.disabled = false;
btn.textContent = 'Add Group';
});
// Connected Apps functionality
async function fetchApps() {
appsTableBody.innerHTML = '<tr><td colspan="4" class="text-center">Loading...</td></tr>';
try {
const res = await fetch('api/apps');
const data = await res.json();
if (!data.success) {
appsTableBody.innerHTML = `<tr><td colspan="4" class="text-center">Error fetching apps</td></tr>`;
return;
}
if (data.apps.length === 0) {
appsTableBody.innerHTML = '<tr><td colspan="4" class="text-center">No apps connected yet. Send a message to see an app appear here!</td></tr>';
return;
}
appsTableBody.innerHTML = '';
data.apps.sort((a, b) => new Date(b.lastSeen) - new Date(a.lastSeen)).forEach(app => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td><strong>${app.name}</strong></td>
<td>${app.messageCount}</td>
<td>${new Date(app.lastSeen).toLocaleString()}</td>
<td><small style="color:var(--text-muted)">${app.lastIp}</small></td>
`;
appsTableBody.appendChild(tr);
});
} catch (e) {
appsTableBody.innerHTML = `<tr><td colspan="4" class="text-center">Fetch failed.</td></tr>`;
}
}
document.getElementById('btn-refresh-apps').addEventListener('click', fetchApps);
document.querySelector('.close-modal').addEventListener('click', () => {
inviteModal.classList.remove('active');
});
document.getElementById('btn-copy-link').addEventListener('click', () => {
inviteLinkInput.select();
document.execCommand('copy');
alert('Copied to clipboard!');
});
// Test Message
document.getElementById('test-msg-form').addEventListener('submit', async (e) => {
e.preventDefault();
const btn = e.target.querySelector('button');
const resultDiv = document.getElementById('test-result');
btn.textContent = 'Sending...';
btn.disabled = true;
resultDiv.textContent = '';
const to = document.getElementById('test-to').value;
const message = document.getElementById('test-body').value;
try {
const res = await fetch('api/send-message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ to, message })
});
const data = await res.json();
if (data.success) {
resultDiv.innerHTML = `<span style="color:var(--success)">✅ Sent successfully (ID: ${data.messageId})</span>`;
document.getElementById('test-body').value = '';
} else {
resultDiv.innerHTML = `<span style="color:var(--error)">❌ ${data.error}</span>`;
}
} catch (err) {
resultDiv.innerHTML = `<span style="color:var(--error)">❌ Request failed.</span>`;
} finally {
btn.textContent = 'Send Message';
btn.disabled = false;
}
});