280 lines
12 KiB
JavaScript
280 lines
12 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
|
const messageForm = document.getElementById('message-form');
|
|
const messageInput = document.getElementById('message-input');
|
|
const chatBox = document.getElementById('chat-box');
|
|
const typingIndicator = document.getElementById('typing-indicator');
|
|
|
|
let currentUser = '';
|
|
let lastAnimatedMessageId = null;
|
|
|
|
function showTypingIndicator() {
|
|
if (typingIndicator) {
|
|
typingIndicator.style.display = 'block';
|
|
chatBox.scrollTop = chatBox.scrollHeight;
|
|
}
|
|
}
|
|
|
|
function hideTypingIndicator() {
|
|
if (typingIndicator) {
|
|
typingIndicator.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function createAvatar(message) {
|
|
const avatar = document.createElement('div');
|
|
avatar.classList.add('avatar');
|
|
const initial = message.user_initial || (message.username === 'AI' ? 'A' : '?');
|
|
avatar.textContent = initial;
|
|
// Add a color based on the username
|
|
const colors = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b'];
|
|
const colorIndex = message.username.charCodeAt(0) % colors.length;
|
|
avatar.style.backgroundColor = colors[colorIndex];
|
|
return avatar;
|
|
}
|
|
|
|
function renderMessage(message) {
|
|
const isSent = message.username === currentUser;
|
|
if (isSent || message.username === 'AI') {
|
|
hideTypingIndicator();
|
|
}
|
|
|
|
const messageElement = document.createElement('div');
|
|
messageElement.classList.add('message', isSent ? 'sent' : 'received');
|
|
messageElement.dataset.messageId = message.id;
|
|
|
|
const avatar = createAvatar(message);
|
|
messageElement.appendChild(avatar);
|
|
|
|
const messageBody = document.createElement('div');
|
|
messageBody.classList.add('message-body');
|
|
|
|
if (!isSent) {
|
|
const usernameElement = document.createElement('div');
|
|
usernameElement.classList.add('message-username');
|
|
usernameElement.textContent = message.username;
|
|
messageBody.appendChild(usernameElement);
|
|
}
|
|
|
|
const messageP = document.createElement('p');
|
|
messageP.classList.add('message-text');
|
|
messageP.textContent = message.message;
|
|
|
|
const messageTime = document.createElement('span');
|
|
messageTime.classList.add('message-time');
|
|
const time = new Date(message.created_at);
|
|
messageTime.textContent = time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
|
|
messageBody.appendChild(messageP);
|
|
messageBody.appendChild(messageTime);
|
|
messageElement.appendChild(messageBody);
|
|
chatBox.appendChild(messageElement);
|
|
|
|
chatBox.scrollTop = chatBox.scrollHeight;
|
|
}
|
|
|
|
function renderMessageWithStreaming(message) {
|
|
renderMessage({ ...message, message: '' }); // Render the container first
|
|
const messageP = chatBox.querySelector(`[data-message-id="${message.id}"] .message-text`);
|
|
if (!messageP) return;
|
|
|
|
const words = message.message.split(' ');
|
|
let i = 0;
|
|
const interval = setInterval(() => {
|
|
if (i < words.length) {
|
|
messageP.textContent += (i > 0 ? ' ' : '') + words[i];
|
|
chatBox.scrollTop = chatBox.scrollHeight;
|
|
i++;
|
|
} else {
|
|
clearInterval(interval);
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
async function fetchMessages() {
|
|
try {
|
|
const response = await fetch('/api/chat.php');
|
|
if (!response.ok) {
|
|
if (response.status === 401) window.location.href = 'login.php';
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
currentUser = data.currentUser;
|
|
const messages = data.messages || [];
|
|
|
|
const existingMessageIds = new Set([...chatBox.querySelectorAll('.message')].map(m => m.dataset.messageId));
|
|
|
|
messages.forEach(message => {
|
|
if (!existingMessageIds.has(message.id.toString())) {
|
|
const isAI = message.username === 'AI';
|
|
const isNewer = message.id > lastAnimatedMessageId;
|
|
|
|
if (isAI && isNewer) {
|
|
lastAnimatedMessageId = message.id;
|
|
renderMessageWithStreaming(message);
|
|
} else {
|
|
renderMessage(message);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (messages.length > 0) {
|
|
const lastMessage = messages[messages.length - 1];
|
|
if (lastMessage.username !== 'AI') {
|
|
hideTypingIndicator();
|
|
}
|
|
}
|
|
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching messages:', error);
|
|
hideTypingIndicator();
|
|
}
|
|
}
|
|
|
|
async function resetChat() {
|
|
try {
|
|
const response = await fetch('/api/chat.php?action=reset');
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
chatBox.innerHTML = '';
|
|
} else {
|
|
console.error('Error resetting chat:', result.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error resetting chat:', error);
|
|
}
|
|
}
|
|
|
|
if (chatBox) {
|
|
fetchMessages();
|
|
setInterval(fetchMessages, 3000);
|
|
|
|
if (messageForm) {
|
|
messageForm.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
const messageText = messageInput.value.trim();
|
|
if (messageText === '/reset') {
|
|
messageInput.value = '';
|
|
await resetChat();
|
|
return;
|
|
}
|
|
|
|
if (messageText !== '') {
|
|
messageInput.value = '';
|
|
messageInput.focus();
|
|
showTypingIndicator();
|
|
|
|
// Optimistically render user's message
|
|
const tempId = `temp_${new Date().getTime()}`;
|
|
renderMessage({
|
|
id: tempId,
|
|
username: currentUser,
|
|
message: messageText,
|
|
created_at: new Date().toISOString(),
|
|
user_initial: currentUser.charAt(0).toUpperCase()
|
|
});
|
|
|
|
const personality = document.getElementById('ai-personality').value.trim();
|
|
|
|
try {
|
|
const response = await fetch('/api/chat.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: messageText, personality: personality })
|
|
});
|
|
const result = await response.json();
|
|
if (!result.success) {
|
|
console.error('Error saving message:', result.error);
|
|
// Optionally remove the optimistic message or show an error
|
|
const failedMsg = chatBox.querySelector(`[data-message-id="${tempId}"]`);
|
|
if(failedMsg) failedMsg.classList.add('error');
|
|
}
|
|
// The poller will fetch the confirmed message from the server
|
|
} catch (error) {
|
|
console.error('Error sending message:', error);
|
|
hideTypingIndicator();
|
|
const failedMsg = chatBox.querySelector(`[data-message-id="${tempId}"]`);
|
|
if(failedMsg) failedMsg.classList.add('error');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Security Panel Logic
|
|
const vpnConnectBtn = document.getElementById('vpn-connect-btn');
|
|
const vpnStatus = document.getElementById('vpn-status');
|
|
const vpnCountrySelect = document.getElementById('vpn-country');
|
|
|
|
const antivirusScanBtn = document.getElementById('antivirus-scan-btn');
|
|
const antivirusResult = document.getElementById('antivirus-result');
|
|
|
|
let isVpnConnected = false;
|
|
|
|
if (vpnConnectBtn) {
|
|
vpnConnectBtn.addEventListener('click', async () => {
|
|
isVpnConnected = !isVpnConnected;
|
|
|
|
if (isVpnConnected) {
|
|
const selectedCountry = vpnCountrySelect.value;
|
|
vpnConnectBtn.textContent = 'Desconectar';
|
|
vpnConnectBtn.classList.remove('btn-primary');
|
|
vpnConnectBtn.classList.add('btn-danger');
|
|
vpnStatus.innerHTML = '<p class="text-primary">Conectando...</p>';
|
|
|
|
const prompt = `Simule que você está estabelecendo uma conexão VPN para o país ${selectedCountry}. Descreva o processo e, ao final, confirme a conexão segura e forneça um endereço de IP fictício para esse país.`;
|
|
|
|
try {
|
|
const response = await fetch('/api/chat.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: prompt, context: 'vpn' }) // Add context for the API
|
|
});
|
|
const result = await response.json();
|
|
if (result.success && result.data) {
|
|
vpnStatus.innerHTML = `<p class="text-success">${result.data.replace(/\n/g, '<br>')}</p>`;
|
|
} else {
|
|
vpnStatus.innerHTML = '<p class="text-danger">Falha ao conectar. Tente novamente.</p>';
|
|
}
|
|
} catch (error) {
|
|
console.error('VPN connection error:', error);
|
|
vpnStatus.innerHTML = '<p class="text-danger">Ocorreu um erro na simulação.</p>';
|
|
}
|
|
|
|
} else {
|
|
vpnConnectBtn.textContent = 'Conectar';
|
|
vpnConnectBtn.classList.remove('btn-danger');
|
|
vpnConnectBtn.classList.add('btn-primary');
|
|
vpnStatus.innerHTML = '<p class="text-muted">Status: Desconectado</p>';
|
|
}
|
|
});
|
|
}
|
|
|
|
if (antivirusScanBtn) {
|
|
antivirusScanBtn.addEventListener('click', async () => {
|
|
antivirusScanBtn.disabled = true;
|
|
antivirusResult.innerHTML = '<p class="text-primary">Verificando sistema... Isso pode levar um momento.</p>';
|
|
|
|
const prompt = `Simule uma verificação completa de um sistema de computador em busca de vírus e malware. Descreva as etapas da verificação (ex: verificação de memória, arquivos de inicialização, registro, arquivos do sistema). Ao final, apresente um relatório com os resultados, informando se alguma ameaça foi encontrada ou se o sistema está limpo.`;
|
|
|
|
try {
|
|
const response = await fetch('/api/chat.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: prompt, context: 'antivirus' }) // Add context for the API
|
|
});
|
|
const result = await response.json();
|
|
if (result.success && result.data) {
|
|
antivirusResult.innerHTML = `<p class="text-success">${result.data.replace(/\n/g, '<br>')}</p>`;
|
|
} else {
|
|
antivirusResult.innerHTML = '<p class="text-danger">A verificação falhou. Tente novamente.</p>';
|
|
} catch (error) {
|
|
console.error('Antivirus scan error:', error);
|
|
antivirusResult.innerHTML = '<p class="text-danger">Ocorreu um erro na simulação da verificação.</p>';
|
|
} finally {
|
|
antivirusScanBtn.disabled = false;
|
|
}
|
|
});
|
|
}
|
|
}); |