File size: 4,180 Bytes
1408074
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
069c097
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Botchat CVNSS 4.0</title>

<!-- Bộ chuyển CVNSS 4.0 -->
<script src="cvnss4.0-converter.js"></script>

<style>
body{font-family:Arial,Helvetica,sans-serif;background:#f5f7fa;margin:30px auto;max-width:760px;color:#333}
h2{text-align:center;color:#2c3e50;margin-bottom:20px}
#chat{background:#fff;border:1px solid #dcdcdc;border-radius:8px;min-height:340px;max-height:540px;
      overflow-y:auto;padding:18px;margin-bottom:16px}
.msg{margin-bottom:16px;line-height:1.45}
.user b{color:#2980b9}.bot b{color:#27ae60}
#bar{display:flex;gap:8px}
textarea{flex:1;padding:10px 12px;font-size:15px;border:1px solid #ccc;border-radius:6px;resize:vertical;min-height:48px}
button{padding:11px 22px;font-size:15px;border:none;border-radius:6px;cursor:pointer;background:#2c97de;color:#fff}
button:hover{filter:brightness(1.1)}button:active{filter:brightness(.93)}
.spinner{display:inline-block;width:16px;height:16px;border:3px solid #bbb;border-top-color:#2c97de;border-radius:50%;
         animation:spin 1s linear infinite;margin-left:6px;vertical-align:middle}
@keyframes spin{to{transform:rotate(360deg)}}
</style>
</head>
<body>
<h2>💬 Botchat - 1 sản phẩm của CVNSS4.0</h2>

<div id="chat"></div>

<div id="bar">
  <!-- textarea đa dòng; Shift+Enter xuống dòng -->
  <textarea id="input" placeholder="Nhập tin nhắn…"></textarea>
  <button id="send">Gửi</button>
</div>

<script>
/* ========= CẤU HÌNH ========= */
const GROQ_KEY = "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";   // chỉ dùng nội bộ!
const ENDPOINT = "https://api.groq.com/openai/v1/chat/completions";
const MODEL    = "meta-llama/llama-4-scout-17b-16e-instruct";
/* ============================ */

const chat   = document.getElementById("chat");
const input  = document.getElementById("input");
const sendBt = document.getElementById("send");
const spin   = '<span class="spinner"></span>';
const toCVN  = txt => CVNSSConverter.convert(txt,"cqn").cvn;
const scroll = () => chat.scrollTop = chat.scrollHeight;

/* Thêm tin nhắn vào khung chat */
function add(role, html){
  chat.insertAdjacentHTML("beforeend",
    `<div class="msg ${role}"><b>${role==='user'?'Bạn':'GPT'}:</b> ${html}</div>`);
  scroll();
}

/* Gọi Groq API (retry tối đa 3 lần nếu 429/503) */
async function askGroq(prompt){
  const body = JSON.stringify({
    model: MODEL,
    messages: [{ role:"user", content: prompt }]
  });

  for(let i=0;i<3;i++){
    const res = await fetch(ENDPOINT,{
      method:"POST",
      headers:{
        "Content-Type":"application/json",
        "Authorization":`Bearer ${GROQ_KEY}`
      },
      body
    });

    if(res.ok){
      const j = await res.json();
      return j.choices[0].message.content.trim();   // thành công
    }

    if([429,503].includes(res.status)){            // hết quota / quá tải → đợi
      const wait = 1500*(i+1);                     // 1.5s,3s,4.5s
      console.warn(`Groq ${res.status} – đợi ${wait/1000}s rồi thử lại`);
      await new Promise(r=>setTimeout(r,wait));
      continue;
    }
    throw new Error(`HTTP ${res.status}${await res.text()}`);
  }
  throw new Error("Groq quá tải – thử lại sau.");
}

/* Gửi tin nhắn */
async function send(){
  const text = input.value.trim();
  if(!text) return;
  input.value=""; add("user", text); add("bot", spin);
  const last = chat.lastElementChild; sendBt.disabled=true;

  try{
    const raw = await askGroq(text);
    last.innerHTML = `<b>GPT:</b> ${toCVN(raw)}`;
  }catch(err){
    last.innerHTML = `<b>GPT:</b> ⚠️ ${err.message}`;
    console.error(err);
  }finally{
    sendBt.disabled=false; input.focus();
  }
}

/* Sự kiện UI */
sendBt.onclick = send;
input.addEventListener("keydown", e=>{
  if(e.key==="Enter" && !e.shiftKey){              // Enter để gửi
    e.preventDefault(); send();
  }
});

input.focus();

/* Khuyến nghị chạy qua localhost khi mở file trực tiếp */
if(location.protocol==='file:'){
  console.warn("Chạy: python -m http.server 8080  ➔  http://localhost:8080/");
}
</script>
</body>
</html>