Skip to content

Commit 8ca9469

Browse files
Merge pull request #159 from ProjectZeroDays/add-chatbot-frontend
Add chatbot frontend with full screen view
2 parents 0455561 + ceb7665 commit 8ca9469

File tree

3 files changed

+538
-238
lines changed

3 files changed

+538
-238
lines changed

src/chatbot/static/css/styles.css

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,67 @@ button {
4343
button:hover {
4444
background-color: #0056b3;
4545
}
46+
47+
.settings-panel {
48+
position: fixed;
49+
right: -300px;
50+
top: 0;
51+
width: 300px;
52+
height: 100%;
53+
background-color: #1a1a1a;
54+
transition: 0.3s;
55+
padding: 1rem;
56+
overflow-y: auto;
57+
}
58+
59+
.settings-panel.active {
60+
right: 0;
61+
}
62+
63+
.conversation-item {
64+
padding: 0.5rem;
65+
cursor: pointer;
66+
border-bottom: 1px solid #333;
67+
}
68+
69+
.model-selector {
70+
display: flex;
71+
gap: 0.5rem;
72+
flex-wrap: wrap;
73+
margin: 1rem 0;
74+
}
75+
76+
.model-button {
77+
background: #333;
78+
border: 1px solid #444;
79+
color: white;
80+
padding: 0.25rem 0.5rem;
81+
border-radius: 1rem;
82+
cursor: pointer;
83+
}
84+
85+
.error-message {
86+
color: #ff4444;
87+
padding: 0.5rem;
88+
display: none;
89+
}
90+
91+
.voice-recording {
92+
animation: pulse 1s infinite;
93+
}
94+
95+
@keyframes pulse {
96+
0% { opacity: 1; }
97+
50% { opacity: 0.5; }
98+
100% { opacity: 1; }
99+
}
100+
101+
@media (max-width: 768px) {
102+
.input-container {
103+
flex-direction: column;
104+
}
105+
106+
.chat-input {
107+
width: 100%;
108+
}
109+
}

src/chatbot/static/js/script.js

Lines changed: 235 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,254 @@
1-
async function sendMessage() {
2-
const inputField = document.getElementById("user-input");
3-
const userMessage = inputField.value;
4-
inputField.value = '';
5-
appendMessage("You: " + userMessage);
6-
7-
let responseMessage = "I'm sorry, I didn't understand that.";
8-
if (userMessage.toLowerCase().includes("scan networks")) {
9-
const response = await fetch('/scan_network', { method: 'POST' });
10-
const vulnerabilities = await response.json();
11-
responseMessage = "Vulnerabilities found: " + JSON.stringify(vulnerabilities);
12-
} else if (userMessage.toLowerCase().includes("deploy exploit")) {
13-
const target = prompt("Enter target for exploit deployment:");
14-
const response = await fetch('/deploy_exploit', {
1+
const ALLOWED_EXTENSIONS = ['pdf','zip','html','js','java','css','ts','gitignore','env','json','config','txt','rb','py','xml','doc','xls','ppt','7z','rar','db','mhtml','xhtml','pl','pyc'];
2+
let currentFiles = [];
3+
let isRecording = false;
4+
let recognition;
5+
6+
if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
7+
recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
8+
recognition.continuous = false;
9+
recognition.interimResults = false;
10+
11+
recognition.onresult = (event) => {
12+
const transcript = event.results[0][0].transcript;
13+
document.getElementById('user-input').value = transcript;
14+
};
15+
16+
recognition.onerror = (event) => {
17+
console.error('Speech recognition error:', event.error);
18+
};
19+
}
20+
21+
function saveSettings() {
22+
localStorage.setItem('apiEndpoint', document.getElementById('apiEndpoint').value);
23+
localStorage.setItem('githubToken', document.getElementById('githubToken').value);
24+
alert('Settings saved');
25+
}
26+
27+
function loadSettings() {
28+
document.getElementById('apiEndpoint').value = localStorage.getItem('apiEndpoint') || '';
29+
document.getElementById('githubToken').value = localStorage.getItem('githubToken') || '';
30+
}
31+
32+
function handleFileUpload(e) {
33+
const files = Array.from(e.target.files);
34+
const invalidFiles = files.filter(f => {
35+
const ext = f.name.split('.').pop();
36+
return !ALLOWED_EXTENSIONS.includes(ext);
37+
});
38+
39+
if (invalidFiles.length > 0) {
40+
showError(`Invalid file types: ${invalidFiles.map(f => f.name).join(', ')}`);
41+
return;
42+
}
43+
44+
currentFiles = [...currentFiles, ...files];
45+
showError('Files uploaded successfully', 'success');
46+
}
47+
48+
async function exportZip() {
49+
if (currentFiles.length === 0) {
50+
showError('No files to export');
51+
return;
52+
}
53+
54+
const formData = new FormData();
55+
currentFiles.forEach((file, i) => formData.append(`file${i}`, file));
56+
57+
try {
58+
const response = await fetch('/api/export-zip', {
1559
method: 'POST',
16-
headers: {
17-
'Content-Type': 'application/json'
18-
},
19-
body: JSON.stringify({ target })
60+
body: formData
2061
});
21-
const result = await response.json();
22-
responseMessage = "Exploit deployment result: " + result.result;
23-
} else if (userMessage.toLowerCase().includes("adjust alert thresholds")) {
24-
const response = await fetch('/adjust_alert_thresholds', { method: 'POST' });
25-
const result = await response.json();
26-
responseMessage = "Alert thresholds adjusted: " + result.threshold;
62+
63+
const blob = await response.blob();
64+
const url = window.URL.createObjectURL(blob);
65+
const a = document.createElement('a');
66+
a.href = url;
67+
a.download = 'export.zip';
68+
a.click();
69+
} catch (error) {
70+
showError('ZIP export failed');
2771
}
28-
appendMessage("Chatbot: " + responseMessage);
2972
}
3073

31-
async function uploadImage() {
32-
const fileInput = document.getElementById("image-input");
33-
const file = fileInput.files[0];
34-
const formData = new FormData();
35-
formData.append('image', file);
74+
function addMessage(sender, content, isPartial = false) {
75+
const container = document.getElementById('chat-box');
76+
const messageDiv = document.createElement('div');
77+
messageDiv.className = `message ${sender}-message`;
78+
79+
messageDiv.innerHTML = `
80+
<div>${content}</div>
81+
<div class="message-actions">
82+
<button onclick="copyToClipboard('${content}')"><i class="fas fa-copy"></i></button>
83+
<button onclick="shareMessage('${content}')"><i class="fas fa-share"></i></button>
84+
${sender === 'ai' ? `<button onclick="downloadCode('${content}')"><i class="fas fa-download"></i></button>` : ''}
85+
</div>
86+
${isPartial ? '<button class="continue-button" onclick="continueResponse()">Continue</button>' : ''}
87+
`;
3688

37-
const response = await fetch('/extract_text_from_image', {
38-
method: 'POST',
39-
body: formData
40-
});
41-
const data = await response.json();
42-
appendMessage("Extracted Text: " + data.extracted_text);
89+
container.appendChild(messageDiv);
90+
container.scrollTop = container.scrollHeight;
4391
}
4492

45-
async function uploadDocument() {
46-
const fileInput = document.getElementById("document-input");
47-
const file = fileInput.files[0];
48-
const formData = new FormData();
49-
formData.append('document', file);
93+
async function continueResponse() {
94+
try {
95+
const response = await fetch('/continue_response', {
96+
method: 'POST'
97+
});
98+
99+
const data = await response.json();
100+
addMessage('ai', data.response, data.isPartial);
101+
} catch (error) {
102+
showError('Error continuing response');
103+
}
104+
}
105+
106+
function toggleVoice() {
107+
if (!recognition) {
108+
showError('Speech recognition not supported');
109+
return;
110+
}
50111

51-
const response = await fetch('/parse_document', {
52-
method: 'POST',
53-
body: formData
112+
isRecording = !isRecording;
113+
const micButton = document.querySelector('.fa-microphone');
114+
115+
if (isRecording) {
116+
recognition.start();
117+
micButton.classList.add('voice-recording');
118+
} else {
119+
recognition.stop();
120+
micButton.classList.remove('voice-recording');
121+
}
122+
}
123+
124+
function showError(message, type = 'error') {
125+
const errorDiv = document.createElement('div');
126+
errorDiv.className = `error-message ${type}`;
127+
errorDiv.textContent = message;
128+
document.body.appendChild(errorDiv);
129+
130+
setTimeout(() => errorDiv.remove(), 5000);
131+
}
132+
133+
document.addEventListener('DOMContentLoaded', () => {
134+
loadSettings();
135+
});
136+
137+
function copyToClipboard(content) {
138+
navigator.clipboard.writeText(content).then(() => {
139+
showError('Copied to clipboard', 'success');
140+
}).catch(err => {
141+
showError('Failed to copy', 'error');
54142
});
55-
const data = await response.json();
56-
appendMessage("Parsed Text: " + data.parsed_text);
57143
}
58144

59-
function appendMessage(message) {
60-
const chatBox = document.getElementById("chat-box");
61-
chatBox.innerHTML += "<div>" + message + "</div>";
62-
chatBox.scrollTop = chatBox.scrollHeight; // Auto-scroll
145+
function shareMessage(content) {
146+
if (navigator.share) {
147+
navigator.share({
148+
title: 'Chat Message',
149+
text: content
150+
}).then(() => {
151+
showError('Message shared', 'success');
152+
}).catch(err => {
153+
showError('Failed to share', 'error');
154+
});
155+
} else {
156+
showError('Share not supported', 'error');
157+
}
63158
}
64-
appendMessage("Chatbot: " + responseMessage);
159+
160+
function downloadCode(content) {
161+
const blob = new Blob([content], { type: 'text/plain' });
162+
const url = window.URL.createObjectURL(blob);
163+
const a = document.createElement('a');
164+
a.href = url;
165+
a.download = `code-${Date.now()}.txt`;
166+
a.click();
65167
}
66168

67-
async function uploadImage() {
68-
const fileInput = document.getElementById("image-input");
69-
const file = fileInput.files[0];
70-
const formData = new FormData();
71-
formData.append('image', file);
169+
function toggleDarkMode() {
170+
document.body.classList.toggle('light-mode');
171+
localStorage.setItem('darkMode', document.body.classList.contains('light-mode') ? 'false' : 'true');
172+
}
173+
174+
function handleImageUpload() {
175+
const input = document.getElementById('image-upload');
176+
const file = input.files[0];
177+
if (file) {
178+
const reader = new FileReader();
179+
reader.onload = function(e) {
180+
const img = document.createElement('img');
181+
img.src = e.target.result;
182+
document.getElementById('chat-box').appendChild(img);
183+
};
184+
reader.readAsDataURL(file);
185+
}
186+
}
187+
188+
function handleDocUpload() {
189+
const input = document.getElementById('doc-upload');
190+
const file = input.files[0];
191+
if (file) {
192+
const reader = new FileReader();
193+
reader.onload = function(e) {
194+
const text = e.target.result;
195+
addMessage('user', text);
196+
};
197+
reader.readAsText(file);
198+
}
199+
}
72200

73-
const response = await fetch('/extract_text_from_image', {
74-
method: 'POST',
75-
body: formData
201+
function switchMode(mode) {
202+
const modes = ['ai-chat', 'red-team', 'blue-team', 'purple-team', 'developer', 'foia-manager', 'support'];
203+
modes.forEach(m => document.body.classList.remove(m));
204+
document.body.classList.add(mode);
205+
showError(`Switched to ${mode} mode`, 'success');
206+
}
207+
208+
function loadPreviousConversations() {
209+
const conversations = JSON.parse(localStorage.getItem('conversations')) || [];
210+
const container = document.getElementById('previous-conversations');
211+
container.innerHTML = '';
212+
conversations.forEach((conv, index) => {
213+
const item = document.createElement('div');
214+
item.className = 'conversation-item';
215+
item.textContent = `Conversation ${index + 1}`;
216+
item.onclick = () => loadConversation(index);
217+
container.appendChild(item);
76218
});
77-
const data = await response.json();
78-
appendMessage("Extracted Text: " + data.extracted_text);
79219
}
80220

221+
function loadConversation(index) {
222+
const conversations = JSON.parse(localStorage.getItem('conversations')) || [];
223+
const conversation = conversations[index];
224+
const container = document.getElementById('chat-box');
225+
container.innerHTML = '';
226+
conversation.forEach(msg => addMessage(msg.sender, msg.content));
227+
}
228+
229+
function saveConversation() {
230+
const messages = Array.from(document.querySelectorAll('.message')).map(msg => ({
231+
sender: msg.classList.contains('user-message') ? 'user' : 'ai',
232+
content: msg.querySelector('div').textContent
233+
}));
234+
const conversations = JSON.parse(localStorage.getItem('conversations')) || [];
235+
conversations.push(messages);
236+
localStorage.setItem('conversations', JSON.stringify(conversations));
237+
showError('Conversation saved', 'success');
238+
}
239+
240+
function deleteConversation(index) {
241+
const conversations = JSON.parse(localStorage.getItem('conversations')) || [];
242+
conversations.splice(index, 1);
243+
localStorage.setItem('conversations', JSON.stringify(conversations));
244+
loadPreviousConversations();
245+
showError('Conversation deleted', 'success');
246+
}
247+
248+
document.addEventListener('DOMContentLoaded', () => {
249+
loadSettings();
250+
loadPreviousConversations();
251+
if (localStorage.getItem('darkMode') === 'true') {
252+
document.body.classList.add('dark-mode');
253+
}
254+
});

0 commit comments

Comments
 (0)