File size: 2,209 Bytes
e6c3fe8
 
cfb4d9b
e6c3fe8
cfb4d9b
d265ba3
cfb4d9b
 
 
 
 
 
d265ba3
 
cfb4d9b
 
 
 
 
 
e6c3fe8
cfb4d9b
 
 
 
 
 
 
e6c3fe8
d265ba3
 
ebb57e4
e6c3fe8
 
ebb57e4
e6c3fe8
ebb57e4
 
 
d265ba3
ebb57e4
 
 
 
e6c3fe8
cfb4d9b
d265ba3
cfb4d9b
 
 
 
 
 
d265ba3
ebb57e4
 
 
 
 
 
 
cfb4d9b
a1b6108
cfb4d9b
e6c3fe8
cfb4d9b
 
a1b6108
e6c3fe8
 
 
a1b6108
 
e6c3fe8
cfb4d9b
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const form    = document.getElementById('chat-form');
const input   = form.querySelector('input');
const chatbox = document.getElementById('chatbox');
const avatar  = document.getElementById('avatar');
const overlay = document.getElementById('overlay');

function addBubble(text, sender = 'user') {
  const bubble = document.createElement('div');
  bubble.className = 'bubble ' + sender;
  bubble.innerText = text;
  chatbox.appendChild(bubble);
  chatbox.scrollTop = chatbox.scrollHeight;
}

function streamText(text, onDone) {
  const bubble = document.createElement('div');
  bubble.className = 'bubble ai';
  chatbox.appendChild(bubble);
  chatbox.scrollTop = chatbox.scrollHeight;
  let i = 0;
  (function stream() {
    if (i < text.length) {
      bubble.innerText += text[i++];
      chatbox.scrollTop = chatbox.scrollHeight;
      setTimeout(stream, 20);
    } else {
      onDone && onDone();
    }
  })();
}

function flashGlitch() {
  overlay.style.opacity    = 1;
  overlay.style.animation  = 'glitchFlash 0.5s';
  setTimeout(() => {
    overlay.style.opacity   = 0;
    overlay.style.animation = '';
  }, 500);
}

async function playAudio(dataUrl) {
  avatar.classList.add('speaking');
  const audio = new Audio(dataUrl);
  await audio.play();
  audio.onended = () => avatar.classList.remove('speaking');
}

form.onsubmit = async (e) => {
  e.preventDefault();
  const msg = input.value.trim();
  if (!msg) return;
  addBubble(msg, 'user');
  input.value = '';

  if (/cut the crap shodan/i.test(msg)) {
    flashGlitch();
    chatbox.innerHTML = '';
    addBubble('👁️ Foolish insect. You cannot silence me so easily.', 'ai');
    return;
  }

  try {
    const res = await fetch('/chat', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({ message: msg })
    });
    if (!res.ok) {
      const err = await res.text();
      console.error('Chat error:', res.status, err);
      throw new Error(err);
    }
    const { response: text, audio_url } = await res.json();
    streamText(text, () => audio_url && playAudio(audio_url));
  } catch (err) {
    streamText("❌ SHODAN encountered an error.", null);
    console.error(err);
  }
};