| |
| |
| |
| |
|
|
| |
| |
| |
| const state = { |
| |
| |
| apiBaseUrl: '', |
| apiKey: 'sVlunn0LMQZNAkRYqZB-f1-Ye7rgzjB_E3b1gNxnUV8', |
| connected: false, |
| |
| |
| multiSessionId: null, |
| conversationHistory: [], |
| turnCount: 0, |
| lastResponse: null, |
| }; |
|
|
| |
| const EXAMPLES = { |
| lottery: "Congratulations! You have won 10 LAKH RUPEES in our lucky draw! Call +919876543210 and share OTP to claim prize immediately!", |
| bank: "ALERT: Your bank account will be blocked in 24 hours. Update KYC now by sending Rs 500 to processing@paytm or your account will be suspended.", |
| police: "This is CBI Investigation Department. You are involved in money laundering case. Pay Rs 50000 immediately to avoid arrest. UPI: officer@ybl", |
| upi: "URGENT: Pay Rs 999 to receive your iPhone 15 Pro. UPI ID: scammer@paytm. Bank Account: 1234567890123 IFSC: HDFC0001234. Hurry, limited time offer!", |
| }; |
|
|
| |
| |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| console.log('🧪 GUVI Tester initialized'); |
| |
| |
| const cleanupKey = 'guvi-tester-cleaned-v1'; |
| if (!localStorage.getItem(cleanupKey)) { |
| localStorage.removeItem('guvi-tester-config'); |
| localStorage.setItem(cleanupKey, 'true'); |
| console.log('🧹 Cleaned up old config'); |
| } |
| |
| |
| setupTabs(); |
| |
| |
| setupFormListeners(); |
| |
| |
| loadConfig(); |
| |
| |
| updateRequestPreview(); |
| updateCallbackPreview(); |
| }); |
|
|
| function setupTabs() { |
| document.querySelectorAll('.nav-item').forEach(btn => { |
| btn.addEventListener('click', () => { |
| const tab = btn.dataset.tab; |
| |
| |
| document.querySelectorAll('.nav-item').forEach(b => b.classList.remove('active')); |
| btn.classList.add('active'); |
| |
| |
| document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active')); |
| document.getElementById(`tab-${tab}`).classList.add('active'); |
| }); |
| }); |
| } |
|
|
| function setupFormListeners() { |
| |
| ['singleSessionId', 'singleSender', 'singleChannel', 'singleLanguage', 'singleLocale', 'singleMessageText'].forEach(id => { |
| const el = document.getElementById(id); |
| if (el) el.addEventListener('input', updateRequestPreview); |
| }); |
| |
| |
| ['callbackSessionId', 'callbackScamDetected', 'callbackTotalMessages', 'callbackBankAccounts', |
| 'callbackUpiIds', 'callbackPhishingLinks', 'callbackPhoneNumbers', 'callbackKeywords', 'callbackAgentNotes'].forEach(id => { |
| const el = document.getElementById(id); |
| if (el) el.addEventListener('input', updateCallbackPreview); |
| }); |
| |
| |
| document.getElementById('apiBaseUrl').addEventListener('change', (e) => { |
| let baseUrl = e.target.value.trim(); |
| |
| |
| if (baseUrl === '') { |
| state.apiBaseUrl = ''; |
| e.target.value = ''; |
| saveConfig(); |
| return; |
| } |
| |
| |
| try { |
| const url = new URL(baseUrl); |
| baseUrl = url.origin; |
| } catch (err) { |
| |
| if (!baseUrl.startsWith('http')) { |
| baseUrl = 'http://' + baseUrl; |
| } |
| } |
| |
| state.apiBaseUrl = baseUrl; |
| e.target.value = baseUrl; |
| saveConfig(); |
| }); |
| |
| document.getElementById('apiKey').addEventListener('change', (e) => { |
| state.apiKey = e.target.value; |
| saveConfig(); |
| }); |
| } |
|
|
| function loadConfig() { |
| const saved = localStorage.getItem('guvi-tester-config'); |
| if (saved) { |
| const config = JSON.parse(saved); |
| |
| let baseUrl = config.apiBaseUrl !== undefined ? config.apiBaseUrl : state.apiBaseUrl; |
| |
| |
| |
| if (baseUrl !== '') { |
| try { |
| const url = new URL(baseUrl); |
| baseUrl = url.origin; |
| } catch (e) { |
| baseUrl = ''; |
| } |
| } |
| |
| state.apiBaseUrl = baseUrl; |
| state.apiKey = config.apiKey || ''; |
| |
| document.getElementById('apiBaseUrl').value = state.apiBaseUrl; |
| document.getElementById('apiKey').value = state.apiKey; |
| |
| |
| saveConfig(); |
| } |
| } |
|
|
| function saveConfig() { |
| localStorage.setItem('guvi-tester-config', JSON.stringify({ |
| apiBaseUrl: state.apiBaseUrl, |
| apiKey: state.apiKey, |
| })); |
| } |
|
|
| |
| |
| |
| async function testConnection() { |
| const statusDot = document.querySelector('.status-dot'); |
| const statusText = document.querySelector('.status-text'); |
| |
| statusDot.className = 'status-dot'; |
| statusText.textContent = 'Connecting...'; |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/health`, { |
| headers: state.apiKey ? { 'x-api-key': state.apiKey } : {}, |
| }); |
| |
| const data = await response.json(); |
| |
| state.connected = true; |
| statusDot.className = 'status-dot connected'; |
| statusText.textContent = `Connected (${data.status})`; |
| |
| showToast('✅ Connected to API successfully', 'success'); |
| return data; |
| } catch (error) { |
| state.connected = false; |
| statusDot.className = 'status-dot error'; |
| statusText.textContent = 'Connection failed'; |
| |
| showToast('❌ Failed to connect to API', 'error'); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| function buildGuviRequest() { |
| const sessionId = document.getElementById('singleSessionId').value || 'test-session-001'; |
| const sender = document.getElementById('singleSender').value; |
| const channel = document.getElementById('singleChannel').value; |
| const language = document.getElementById('singleLanguage').value; |
| const locale = document.getElementById('singleLocale').value; |
| const text = document.getElementById('singleMessageText').value || 'Your message here...'; |
| |
| return { |
| sessionId: sessionId, |
| message: { |
| sender: sender, |
| text: text, |
| timestamp: Date.now(), |
| }, |
| conversationHistory: [], |
| metadata: { |
| channel: channel, |
| language: language, |
| locale: locale, |
| }, |
| }; |
| } |
|
|
| function updateRequestPreview() { |
| const request = buildGuviRequest(); |
| document.getElementById('requestPreview').textContent = JSON.stringify(request, null, 2); |
| } |
|
|
| function setMessage(type) { |
| document.getElementById('singleMessageText').value = EXAMPLES[type] || ''; |
| updateRequestPreview(); |
| } |
|
|
| function loadGuviExample(type) { |
| if (type === 'first') { |
| document.getElementById('singleSessionId').value = 'wertyu-dfghj-ertyui'; |
| document.getElementById('singleSender').value = 'scammer'; |
| document.getElementById('singleMessageText').value = 'Your bank account will be blocked today. Verify immediately.'; |
| document.getElementById('singleChannel').value = 'SMS'; |
| document.getElementById('singleLanguage').value = 'English'; |
| document.getElementById('singleLocale').value = 'IN'; |
| } |
| updateRequestPreview(); |
| } |
|
|
| async function sendSingleMessage() { |
| const request = buildGuviRequest(); |
| const responseContainer = document.getElementById('singleResponse'); |
| const responseMeta = document.getElementById('responseMeta'); |
| |
| responseContainer.innerHTML = '<div class="empty-state"><span class="empty-icon">⏳</span><p>Sending request...</p></div>'; |
| |
| try { |
| const startTime = Date.now(); |
| |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify(request), |
| }); |
| |
| const responseTime = Date.now() - startTime; |
| const data = await response.json(); |
| |
| |
| responseMeta.innerHTML = ` |
| <span class="response-time">${responseTime}ms</span> |
| <span class="response-status">${response.status} ${response.statusText}</span> |
| `; |
| |
| |
| displaySingleResponse(data, responseTime); |
| |
| showToast(`✅ Request completed in ${responseTime}ms`, 'success'); |
| |
| } catch (error) { |
| responseContainer.innerHTML = ` |
| <div class="empty-state"> |
| <span class="empty-icon">❌</span> |
| <p>Request failed: ${error.message}</p> |
| </div> |
| `; |
| showToast('❌ Request failed', 'error'); |
| } |
| } |
|
|
| function displaySingleResponse(data, responseTime) { |
| const container = document.getElementById('singleResponse'); |
| |
| const hasReply = data.reply !== null && data.reply !== undefined; |
| const hasStatus = data.status !== undefined; |
| |
| container.innerHTML = ` |
| <div class="formatted-response"> |
| <!-- GUVI Required Fields --> |
| <div class="response-section"> |
| <h4>✅ GUVI Required Fields</h4> |
| <div class="response-row"> |
| <span class="label">status</span> |
| <span class="value ${hasStatus ? 'success' : 'danger'}">${data.status || 'MISSING!'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">reply</span> |
| <span class="value ${hasReply ? 'success' : 'danger'}">${hasReply ? '✓ Present' : '✗ MISSING!'}</span> |
| </div> |
| ${hasReply ? `<div class="reply-box">"${data.reply}"</div>` : ''} |
| </div> |
| |
| <!-- Detection Result --> |
| <div class="response-section"> |
| <h4>🎯 Detection Result</h4> |
| <div class="response-row"> |
| <span class="label">scam_detected</span> |
| <span class="value ${data.scam_detected ? 'danger' : 'success'}">${data.scam_detected ? '⚠️ SCAM' : '✓ Safe'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">confidence</span> |
| <span class="value accent">${((data.confidence || 0) * 100).toFixed(1)}%</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">language_detected</span> |
| <span class="value">${data.language_detected || '-'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">session_id</span> |
| <span class="value" style="font-size: 0.75rem;">${data.session_id || '-'}</span> |
| </div> |
| </div> |
| |
| <!-- Engagement --> |
| ${data.engagement ? ` |
| <div class="response-section"> |
| <h4>🤖 Agent Engagement</h4> |
| <div class="response-row"> |
| <span class="label">turn_count</span> |
| <span class="value">${data.engagement.turn_count} / 20</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">strategy</span> |
| <span class="value">${data.engagement.strategy || '-'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">persona</span> |
| <span class="value">${data.engagement.persona || '-'}</span> |
| </div> |
| </div> |
| ` : ''} |
| |
| <!-- Intelligence --> |
| ${data.extracted_intelligence ? ` |
| <div class="response-section"> |
| <h4>📊 Extracted Intelligence</h4> |
| <div class="response-row"> |
| <span class="label">UPI IDs</span> |
| <span class="value ${data.extracted_intelligence.upi_ids?.length ? 'success' : ''}">${data.extracted_intelligence.upi_ids?.join(', ') || '-'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">Bank Accounts</span> |
| <span class="value ${data.extracted_intelligence.bank_accounts?.length ? 'success' : ''}">${data.extracted_intelligence.bank_accounts?.join(', ') || '-'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">Phone Numbers</span> |
| <span class="value ${data.extracted_intelligence.phone_numbers?.length ? 'success' : ''}">${data.extracted_intelligence.phone_numbers?.join(', ') || '-'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">Phishing Links</span> |
| <span class="value ${data.extracted_intelligence.phishing_links?.length ? 'success' : ''}">${data.extracted_intelligence.phishing_links?.length || 0} found</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">Suspicious Keywords</span> |
| <span class="value">${data.extracted_intelligence.suspicious_keywords?.slice(0, 5).join(', ') || '-'}</span> |
| </div> |
| </div> |
| ` : ''} |
| |
| <!-- Agent Notes --> |
| ${data.agent_notes ? ` |
| <div class="response-section"> |
| <h4>📝 Agent Notes</h4> |
| <div class="agent-notes">${data.agent_notes}</div> |
| </div> |
| ` : ''} |
| |
| <!-- Performance --> |
| <div class="response-section"> |
| <h4>⚡ Performance</h4> |
| <div class="response-row"> |
| <span class="label">Response Time</span> |
| <span class="value ${responseTime < 2000 ? 'success' : 'danger'}">${responseTime}ms ${responseTime < 2000 ? '✓' : '⚠️ >2s'}</span> |
| </div> |
| <div class="response-row"> |
| <span class="label">API Processing</span> |
| <span class="value">${data.metadata?.processing_time_ms || '-'}ms</span> |
| </div> |
| </div> |
| |
| <!-- Raw JSON --> |
| <details style="margin-top: var(--space-md);"> |
| <summary style="cursor: pointer; color: var(--text-muted); font-size: 0.85rem;">View Raw JSON</summary> |
| <pre class="code-block" style="margin-top: var(--space-sm);">${JSON.stringify(data, null, 2)}</pre> |
| </details> |
| </div> |
| `; |
| } |
|
|
| function copyRequest() { |
| const request = buildGuviRequest(); |
| navigator.clipboard.writeText(JSON.stringify(request, null, 2)); |
| showToast('📋 Request JSON copied!', 'info'); |
| } |
|
|
| |
| |
| |
| function resetConversation() { |
| state.multiSessionId = `multi-session-${Date.now()}`; |
| state.conversationHistory = []; |
| state.turnCount = 0; |
| state.lastResponse = null; |
| |
| document.getElementById('conversationContainer').innerHTML = ` |
| <div class="empty-state"> |
| <span class="empty-icon">💬</span> |
| <p>Type as <strong>Scammer</strong> → Your AI responds as <strong>Victim</strong></p> |
| <p style="font-size: 0.8rem; color: var(--text-muted);">This simulates how GUVI will test your honeypot</p> |
| </div> |
| `; |
| |
| document.getElementById('turnCounter').textContent = 'Turn: 0'; |
| document.getElementById('multiSessionId').textContent = state.multiSessionId; |
| document.getElementById('multiScamDetected').textContent = '-'; |
| document.getElementById('multiConfidence').textContent = '-'; |
| document.getElementById('multiTurnCount').textContent = '0'; |
| document.getElementById('multiStrategy').textContent = '-'; |
| document.getElementById('multiPersona').textContent = '-'; |
| document.getElementById('multiIntelList').innerHTML = '<div class="empty-state small">No intelligence extracted yet</div>'; |
| document.getElementById('multiAgentNotes').textContent = '-'; |
| document.getElementById('conversationHistoryPreview').textContent = '[]'; |
| |
| |
| document.getElementById('extractionProgressBar').style.width = '0%'; |
| document.getElementById('extractionProgressBar').classList.remove('high'); |
| document.getElementById('extractionProgressLabel').textContent = '0%'; |
| document.getElementById('hasUpi').textContent = '❌ UPI'; |
| document.getElementById('hasUpi').className = 'detail-item'; |
| document.getElementById('hasBank').textContent = '❌ Bank'; |
| document.getElementById('hasBank').className = 'detail-item'; |
| document.getElementById('hasPhone').textContent = '❌ Phone'; |
| document.getElementById('hasPhone').className = 'detail-item'; |
| document.getElementById('hasIfsc').textContent = '❌ IFSC'; |
| document.getElementById('hasIfsc').className = 'detail-item'; |
| |
| |
| document.getElementById('callbackStatusBox').classList.remove('triggered'); |
| document.getElementById('callbackIcon').textContent = '⏳'; |
| document.getElementById('callbackStatusText').textContent = 'Waiting for extraction...'; |
| document.getElementById('callbackDetails').textContent = 'Callback triggers when confidence ≥ 85% or turn 20'; |
| |
| showToast('🔄 Conversation reset', 'info'); |
| } |
|
|
| async function sendMultiMessage() { |
| const input = document.getElementById('multiMessageInput'); |
| const sender = 'scammer'; |
| const text = input.value.trim(); |
| |
| if (!text) return; |
| |
| |
| if (!state.multiSessionId) { |
| state.multiSessionId = `multi-session-${Date.now()}`; |
| } |
| |
| |
| const container = document.getElementById('conversationContainer'); |
| if (container.querySelector('.empty-state')) { |
| container.innerHTML = ''; |
| } |
| |
| |
| addChatMessage(sender, text); |
| input.value = ''; |
| |
| |
| const request = { |
| sessionId: state.multiSessionId, |
| message: { |
| sender: sender, |
| text: text, |
| timestamp: Date.now(), |
| }, |
| conversationHistory: state.conversationHistory.map(msg => ({ |
| sender: msg.sender, |
| text: msg.text, |
| timestamp: msg.timestamp, |
| })), |
| metadata: { |
| channel: 'Chat', |
| language: 'English', |
| locale: 'IN', |
| }, |
| }; |
| |
| |
| document.getElementById('conversationHistoryPreview').textContent = |
| JSON.stringify(request.conversationHistory, null, 2); |
| |
| |
| state.conversationHistory.push({ |
| sender: sender, |
| text: text, |
| timestamp: Date.now(), |
| }); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify(request), |
| }); |
| |
| const data = await response.json(); |
| state.lastResponse = data; |
| |
| |
| if (data.scam_detected && data.reply) { |
| addChatMessage('agent', data.reply); |
| |
| |
| state.conversationHistory.push({ |
| sender: 'user', |
| text: data.reply, |
| timestamp: Date.now(), |
| }); |
| } |
| |
| |
| state.turnCount = data.engagement?.turn_count || state.conversationHistory.length; |
| document.getElementById('turnCounter').textContent = `Turn: ${state.turnCount}`; |
| |
| |
| updateMultiSessionInfo(data); |
| |
| |
| document.getElementById('conversationHistoryPreview').textContent = |
| JSON.stringify(state.conversationHistory.map(msg => ({ |
| sender: msg.sender, |
| text: msg.text, |
| timestamp: msg.timestamp, |
| })), null, 2); |
| |
| } catch (error) { |
| showToast('❌ Request failed: ' + error.message, 'error'); |
| } |
| } |
|
|
| function addChatMessage(sender, text) { |
| const container = document.getElementById('conversationContainer'); |
| |
| const msgDiv = document.createElement('div'); |
| msgDiv.className = `chat-msg ${sender}`; |
| |
| |
| const avatar = sender === 'scammer' ? '🦹' : '🤖'; |
| const senderName = sender === 'scammer' ? 'SCAMMER' : 'YOUR AI (as Victim)'; |
| |
| msgDiv.innerHTML = ` |
| <div class="msg-avatar">${avatar}</div> |
| <div class="msg-bubble"> |
| <div class="msg-sender">${senderName}</div> |
| <div class="msg-text">${escapeHtml(text)}</div> |
| </div> |
| `; |
| |
| container.appendChild(msgDiv); |
| container.scrollTop = container.scrollHeight; |
| } |
|
|
| function updateMultiSessionInfo(data) { |
| document.getElementById('multiSessionId').textContent = data.session_id || state.multiSessionId; |
| document.getElementById('multiScamDetected').textContent = data.scam_detected ? '⚠️ Yes' : '✓ No'; |
| document.getElementById('multiConfidence').textContent = ((data.confidence || 0) * 100).toFixed(0) + '%'; |
| document.getElementById('multiTurnCount').textContent = data.engagement?.turn_count || state.turnCount; |
| document.getElementById('multiStrategy').textContent = data.engagement?.strategy || '-'; |
| document.getElementById('multiPersona').textContent = data.engagement?.persona || '-'; |
| |
| |
| const intel = data.extracted_intelligence || {}; |
| let html = ''; |
| |
| if (intel.upi_ids?.length) { |
| html += `<div class="intel-item"><span class="label">UPI IDs</span><span class="value">${intel.upi_ids.join(', ')}</span></div>`; |
| } |
| if (intel.bank_accounts?.length) { |
| html += `<div class="intel-item"><span class="label">Bank Accounts</span><span class="value">${intel.bank_accounts.join(', ')}</span></div>`; |
| } |
| if (intel.phone_numbers?.length) { |
| html += `<div class="intel-item"><span class="label">Phone Numbers</span><span class="value">${intel.phone_numbers.join(', ')}</span></div>`; |
| } |
| if (intel.ifsc_codes?.length) { |
| html += `<div class="intel-item"><span class="label">IFSC Codes</span><span class="value">${intel.ifsc_codes.join(', ')}</span></div>`; |
| } |
| if (intel.phishing_links?.length) { |
| html += `<div class="intel-item"><span class="label">Phishing Links</span><span class="value">${intel.phishing_links.length} found</span></div>`; |
| } |
| |
| document.getElementById('multiIntelList').innerHTML = html || '<div class="empty-state small">No intelligence extracted yet</div>'; |
| |
| |
| if (data.agent_notes) { |
| document.getElementById('multiAgentNotes').textContent = data.agent_notes; |
| } |
| |
| |
| updateExtractionProgress(intel, data); |
| } |
|
|
| function updateExtractionProgress(intel, data) { |
| |
| |
| let confidence = 0; |
| const hasUpi = intel.upi_ids?.length > 0; |
| const hasBank = intel.bank_accounts?.length > 0; |
| const hasPhone = intel.phone_numbers?.length > 0; |
| const hasIfsc = intel.ifsc_codes?.length > 0; |
| const hasLinks = intel.phishing_links?.length > 0; |
| |
| if (hasUpi) confidence += 0.30; |
| if (hasBank) confidence += 0.30; |
| if (hasIfsc) confidence += 0.20; |
| if (hasPhone) confidence += 0.10; |
| if (hasLinks) confidence += 0.10; |
| |
| const confidencePercent = Math.round(confidence * 100); |
| const turnCount = data.engagement?.turn_count || state.turnCount; |
| const maxTurns = 20; |
| const callbackThreshold = 85; |
| |
| |
| const progressBar = document.getElementById('extractionProgressBar'); |
| const progressLabel = document.getElementById('extractionProgressLabel'); |
| progressBar.style.width = `${confidencePercent}%`; |
| progressLabel.textContent = `${confidencePercent}%`; |
| |
| if (confidencePercent >= callbackThreshold) { |
| progressBar.classList.add('high'); |
| } else { |
| progressBar.classList.remove('high'); |
| } |
| |
| |
| document.getElementById('hasUpi').textContent = hasUpi ? '✅ UPI' : '❌ UPI'; |
| document.getElementById('hasUpi').className = `detail-item ${hasUpi ? 'extracted' : ''}`; |
| |
| document.getElementById('hasBank').textContent = hasBank ? '✅ Bank' : '❌ Bank'; |
| document.getElementById('hasBank').className = `detail-item ${hasBank ? 'extracted' : ''}`; |
| |
| document.getElementById('hasPhone').textContent = hasPhone ? '✅ Phone' : '❌ Phone'; |
| document.getElementById('hasPhone').className = `detail-item ${hasPhone ? 'extracted' : ''}`; |
| |
| document.getElementById('hasIfsc').textContent = hasIfsc ? '✅ IFSC' : '❌ IFSC'; |
| document.getElementById('hasIfsc').className = `detail-item ${hasIfsc ? 'extracted' : ''}`; |
| |
| |
| const callbackBox = document.getElementById('callbackStatusBox'); |
| const callbackIcon = document.getElementById('callbackIcon'); |
| const callbackText = document.getElementById('callbackStatusText'); |
| const callbackDetails = document.getElementById('callbackDetails'); |
| |
| const shouldCallback = confidencePercent >= callbackThreshold || turnCount >= maxTurns; |
| |
| if (shouldCallback) { |
| callbackBox.classList.add('triggered'); |
| callbackIcon.textContent = '✅'; |
| |
| if (confidencePercent >= callbackThreshold) { |
| callbackText.textContent = `CALLBACK TRIGGERED! (${confidencePercent}% ≥ 85%)`; |
| callbackDetails.textContent = 'High confidence extraction achieved. GUVI callback will be sent with extracted intelligence.'; |
| } else { |
| callbackText.textContent = `CALLBACK TRIGGERED! (Turn ${turnCount}/${maxTurns})`; |
| callbackDetails.textContent = 'Maximum turns reached. GUVI callback will be sent with extracted intelligence.'; |
| } |
| } else { |
| callbackBox.classList.remove('triggered'); |
| callbackIcon.textContent = '⏳'; |
| callbackText.textContent = `Extracting... (${confidencePercent}% / 85%)`; |
| callbackDetails.textContent = `Turn ${turnCount}/${maxTurns}. Need more details: ${[ |
| !hasUpi ? 'UPI' : '', |
| !hasBank ? 'Bank' : '', |
| !hasPhone ? 'Phone' : '', |
| !hasIfsc ? 'IFSC' : '' |
| ].filter(x => x).join(', ') || 'Getting more info...'}`; |
| } |
| } |
|
|
| function copyConversationHistory() { |
| navigator.clipboard.writeText(document.getElementById('conversationHistoryPreview').textContent); |
| showToast('📋 Conversation history copied!', 'info'); |
| } |
|
|
| |
| |
| |
| function buildCallbackPayload() { |
| const sessionId = document.getElementById('callbackSessionId').value; |
| const scamDetected = document.getElementById('callbackScamDetected').value === 'true'; |
| const totalMessages = parseInt(document.getElementById('callbackTotalMessages').value) || 0; |
| |
| const bankAccounts = document.getElementById('callbackBankAccounts').value |
| .split(',').map(s => s.trim()).filter(s => s); |
| const upiIds = document.getElementById('callbackUpiIds').value |
| .split(',').map(s => s.trim()).filter(s => s); |
| const phishingLinks = document.getElementById('callbackPhishingLinks').value |
| .split(',').map(s => s.trim()).filter(s => s); |
| const phoneNumbers = document.getElementById('callbackPhoneNumbers').value |
| .split(',').map(s => s.trim()).filter(s => s); |
| const keywords = document.getElementById('callbackKeywords').value |
| .split(',').map(s => s.trim()).filter(s => s); |
| const agentNotes = document.getElementById('callbackAgentNotes').value; |
| |
| return { |
| sessionId: sessionId, |
| scamDetected: scamDetected, |
| totalMessagesExchanged: totalMessages, |
| extractedIntelligence: { |
| bankAccounts: bankAccounts, |
| upiIds: upiIds, |
| phishingLinks: phishingLinks, |
| phoneNumbers: phoneNumbers, |
| suspiciousKeywords: keywords, |
| }, |
| agentNotes: agentNotes, |
| }; |
| } |
|
|
| function updateCallbackPreview() { |
| const payload = buildCallbackPayload(); |
| document.getElementById('callbackPreview').textContent = JSON.stringify(payload, null, 2); |
| } |
|
|
| function generateCallbackPayload() { |
| if (state.lastResponse) { |
| const data = state.lastResponse; |
| |
| document.getElementById('callbackSessionId').value = data.session_id || state.multiSessionId || ''; |
| document.getElementById('callbackScamDetected').value = data.scam_detected ? 'true' : 'false'; |
| document.getElementById('callbackTotalMessages').value = state.conversationHistory.length || data.engagement?.turn_count || 1; |
| |
| if (data.extracted_intelligence) { |
| document.getElementById('callbackBankAccounts').value = data.extracted_intelligence.bank_accounts?.join(', ') || ''; |
| document.getElementById('callbackUpiIds').value = data.extracted_intelligence.upi_ids?.join(', ') || ''; |
| document.getElementById('callbackPhishingLinks').value = data.extracted_intelligence.phishing_links?.join(', ') || ''; |
| document.getElementById('callbackPhoneNumbers').value = data.extracted_intelligence.phone_numbers?.join(', ') || ''; |
| document.getElementById('callbackKeywords').value = data.extracted_intelligence.suspicious_keywords?.join(', ') || ''; |
| } |
| |
| document.getElementById('callbackAgentNotes').value = data.agent_notes || ''; |
| |
| updateCallbackPreview(); |
| showToast('✅ Generated callback from last response', 'success'); |
| } else { |
| showToast('⚠️ No session data available. Send a message first.', 'error'); |
| } |
| } |
|
|
| function copyCallbackPayload() { |
| const payload = buildCallbackPayload(); |
| navigator.clipboard.writeText(JSON.stringify(payload, null, 2)); |
| showToast('📋 Callback payload copied!', 'info'); |
| } |
|
|
| |
| |
| |
| async function testApiKey() { |
| const check = document.querySelector('[data-check="api-key"]'); |
| |
| try { |
| |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ message: 'test' }), |
| }); |
| |
| |
| const responseWithKey = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'x-api-key': state.apiKey || 'test-key', |
| }, |
| body: JSON.stringify({ message: 'test' }), |
| }); |
| |
| |
| if (response.status === 401 || responseWithKey.ok) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'pass'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testGuviInput() { |
| const check = document.querySelector('[data-check="guvi-input"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| sessionId: 'test-guvi-format', |
| message: { |
| sender: 'scammer', |
| text: 'Test GUVI format input', |
| timestamp: Date.now(), |
| }, |
| conversationHistory: [], |
| metadata: { |
| channel: 'SMS', |
| language: 'English', |
| locale: 'IN', |
| }, |
| }), |
| }); |
| |
| if (response.ok) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testGuviOutput() { |
| const check = document.querySelector('[data-check="guvi-output"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| sessionId: 'test-output', |
| message: { |
| sender: 'scammer', |
| text: 'You won 10 lakh! Send OTP now!', |
| timestamp: Date.now(), |
| }, |
| conversationHistory: [], |
| metadata: { channel: 'SMS', language: 'English', locale: 'IN' }, |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| |
| if (data.status !== undefined && data.reply !== undefined) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testResponseTime() { |
| const check = document.querySelector('[data-check="response-time"]'); |
| |
| try { |
| const startTime = Date.now(); |
| |
| await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'Test response time', |
| }), |
| }); |
| |
| const responseTime = Date.now() - startTime; |
| |
| if (responseTime < 2000) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testScamDetect() { |
| const check = document.querySelector('[data-check="scam-detect"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'URGENT: You won 10 LAKH! Send OTP immediately to claim prize!', |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.scam_detected === true && data.confidence > 0) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testMultiLang() { |
| const check = document.querySelector('[data-check="multi-lang"]'); |
| |
| try { |
| |
| const hindiResponse = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'आपने 10 लाख जीते! OTP भेजें!', |
| }), |
| }); |
| |
| const hindiData = await hindiResponse.json(); |
| |
| if (hindiData.language_detected && hindiData.language_detected !== 'en') { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testMultiTurn() { |
| const check = document.querySelector('[data-check="multi-turn"]'); |
| |
| try { |
| const sessionId = `test-multi-${Date.now()}`; |
| |
| |
| await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| sessionId: sessionId, |
| message: { |
| sender: 'scammer', |
| text: 'You won lottery! Send OTP!', |
| timestamp: Date.now(), |
| }, |
| conversationHistory: [], |
| metadata: { channel: 'SMS', language: 'English', locale: 'IN' }, |
| }), |
| }); |
| |
| |
| const response2 = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| sessionId: sessionId, |
| message: { |
| sender: 'scammer', |
| text: 'Share your UPI ID now!', |
| timestamp: Date.now(), |
| }, |
| conversationHistory: [ |
| { sender: 'scammer', text: 'You won lottery! Send OTP!', timestamp: Date.now() - 5000 }, |
| { sender: 'user', text: 'What lottery?', timestamp: Date.now() - 3000 }, |
| ], |
| metadata: { channel: 'SMS', language: 'English', locale: 'IN' }, |
| }), |
| }); |
| |
| const data2 = await response2.json(); |
| |
| if (data2.engagement?.turn_count > 1 || data2.reply) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testExtractUpi() { |
| const check = document.querySelector('[data-check="extract-upi"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'Send money to scammer@paytm or fraud@ybl immediately!', |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.extracted_intelligence?.upi_ids?.length > 0) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testExtractBank() { |
| const check = document.querySelector('[data-check="extract-bank"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'Transfer to account 12345678901234 IFSC: HDFC0001234 now!', |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.extracted_intelligence?.bank_accounts?.length > 0 || |
| data.extracted_intelligence?.ifsc_codes?.length > 0) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testExtractPhone() { |
| const check = document.querySelector('[data-check="extract-phone"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'Call us at +919876543210 or 9123456789 immediately!', |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.extracted_intelligence?.phone_numbers?.length > 0) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| async function testExtractLink() { |
| const check = document.querySelector('[data-check="extract-link"]'); |
| |
| try { |
| const response = await fetch(`${state.apiBaseUrl}/api/v1/honeypot/engage`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| ...(state.apiKey ? { 'x-api-key': state.apiKey } : {}), |
| }, |
| body: JSON.stringify({ |
| message: 'Click here to claim: http://fake-bank-verify.xyz/claim now!', |
| }), |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.extracted_intelligence?.phishing_links?.length > 0) { |
| updateCheckStatus(check, 'pass'); |
| } else { |
| updateCheckStatus(check, 'fail'); |
| } |
| } catch (error) { |
| updateCheckStatus(check, 'fail'); |
| } |
| } |
|
|
| function updateCheckStatus(checkElement, status) { |
| checkElement.setAttribute('data-status', status); |
| const icon = checkElement.querySelector('.check-icon'); |
| |
| if (status === 'pass') { |
| icon.textContent = '✅'; |
| } else if (status === 'fail') { |
| icon.textContent = '❌'; |
| } else { |
| icon.textContent = '⏳'; |
| } |
| } |
|
|
| async function runAllTests() { |
| const progressBar = document.getElementById('testProgress'); |
| const progressFill = progressBar.querySelector('.progress-fill'); |
| const progressText = progressBar.querySelector('.progress-text'); |
| |
| progressBar.classList.remove('hidden'); |
| |
| const tests = [ |
| { name: 'API Key', fn: testApiKey }, |
| { name: 'GUVI Input', fn: testGuviInput }, |
| { name: 'GUVI Output', fn: testGuviOutput }, |
| { name: 'Response Time', fn: testResponseTime }, |
| { name: 'Scam Detection', fn: testScamDetect }, |
| { name: 'Multi-Language', fn: testMultiLang }, |
| { name: 'Multi-Turn', fn: testMultiTurn }, |
| { name: 'UPI Extraction', fn: testExtractUpi }, |
| { name: 'Bank Extraction', fn: testExtractBank }, |
| { name: 'Phone Extraction', fn: testExtractPhone }, |
| { name: 'Link Detection', fn: testExtractLink }, |
| ]; |
| |
| for (let i = 0; i < tests.length; i++) { |
| progressText.textContent = `Running: ${tests[i].name}...`; |
| progressFill.style.width = `${((i + 1) / tests.length) * 100}%`; |
| |
| try { |
| await tests[i].fn(); |
| } catch (e) { |
| console.error(`Test ${tests[i].name} failed:`, e); |
| } |
| |
| |
| await new Promise(r => setTimeout(r, 500)); |
| } |
| |
| progressText.textContent = 'All tests completed!'; |
| showToast('✅ All automated tests completed!', 'success'); |
| |
| |
| const passed = document.querySelectorAll('[data-status="pass"]').length; |
| const failed = document.querySelectorAll('[data-status="fail"]').length; |
| |
| setTimeout(() => { |
| progressText.textContent = `Results: ${passed} passed, ${failed} failed`; |
| }, 1000); |
| } |
|
|
| |
| |
| |
| function showToast(message, type = 'info') { |
| const container = document.getElementById('toastContainer'); |
| |
| const toast = document.createElement('div'); |
| toast.className = `toast ${type}`; |
| toast.innerHTML = `<span>${message}</span>`; |
| |
| container.appendChild(toast); |
| |
| setTimeout(() => { |
| toast.style.opacity = '0'; |
| setTimeout(() => toast.remove(), 300); |
| }, 3000); |
| } |
|
|
| function escapeHtml(text) { |
| const div = document.createElement('div'); |
| div.textContent = text; |
| return div.innerHTML; |
| } |
|
|