<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="BIT - Assistente virtual especializado em processos de BizOps e RMAds">
<title>BIT - Guardião de BizOps</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
/* Estilos mantidos do código original */
:root {
--primary: #27ae60;
--primary-light: #2ecc71;
--secondary: #f1c40f;
--secondary-light: #f39c12;
--accent: #8e44ad;
--accent-light: #9b59b6;
--dark: #121212;
--darker: #0a0a0a;
--light: #f5f5f5;
--lighter: #ffffff;
--gray: #2d3436;
--gray-light: #636e72;
--success: #27ae60;
--warning: #f39c12;
--error: #e74c3c;
--info: #3498db;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
line-height: 1.6;
background: linear-gradient(135deg, var(--dark) 0%, #1a1a1a 100%);
color: var(--light);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
.background-particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
}
.particle {
position: absolute;
border-radius: 50%;
animation: floatParticle linear infinite;
}
@keyframes floatParticle {
0% { transform: translateY(0) translateX(0); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(-100vh) translateX(20px); opacity: 0; }
}
@keyframes flutuar-zigzag {
0% { transform: translate(0, 0) rotate(0deg); }
15% { transform: translate(30px, -40px) rotate(8deg); }
30% { transform: translate(60px, 20px) rotate(-6deg); }
45% { transform: translate(30px, 60px) rotate(5deg); }
60% { transform: translate(-30px, 40px) rotate(-7deg); }
75% { transform: translate(-60px, -20px) rotate(6deg); }
90% { transform: translate(-30px, -40px) rotate(-5deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes quicar {
0%, 100% { transform: scale(1); }
50% { transform: scale(0.97); }
}
@keyframes wander {
0% { top: 10%; left: 5%; }
20% { top: 80%; left: 15%; }
40% { top: 20%; left: 85%; }
60% { top: 70%; left: 90%; }
80% { top: 30%; left: 40%; }
100% { top: 10%; left: 5%; }
}
@keyframes corner-hit {
0% { transform: scale(1); }
20% { transform: scale(0.9); }
40% { transform: scale(1.1); }
60% { transform: scale(0.95); }
80% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.hit-animation {
animation: corner-hit 0.8s ease;
}
.bit-widget {
position: relative;
z-index: 100;
font-family: 'Inter', sans-serif;
filter: drop-shadow(0 15px 30px rgba(0, 0, 0, 0.7));
width: 100%;
max-width: 420px;
}
.bit-toggle {
width: 100px;
height: 100px;
border-radius: 70%;
background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 8px 30px rgba(39, 174, 96, 0.6);
border: none;
position: fixed;
bottom: 30px;
right: 30px;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
animation:
flutuar-zigzag 25s infinite ease-in-out,
pulse-shadow 3s infinite,
quicar 3.5s infinite ease-in-out;
overflow: hidden;
outline: none;
}
.bit-toggle.hidden {
display: none;
}
.bit-toggle.wandering {
animation: wander 25s infinite ease-in-out,
pulse-shadow 3s infinite,
quicar 3.5s infinite ease-in-out;
}
.bit-toggle:focus-visible {
box-shadow: 0 0 0 4px rgba(39, 174, 96, 0.5);
}
.bit-toggle:hover {
transform: scale(1.1) rotate(5deg);
}
.bit-toggle::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.5s ease;
}
.bit-toggle:hover::before {
transform: rotate(30deg) translate(20px, 20px);
}
.bit-toggle img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 70%;
border: 2px solid white;
}
.bit-badge {
position: absolute;
top: 12px;
right: 12px;
background-color: var(--secondary);
color: var(--dark);
border-radius: 50%;
width: 26px;
height: 26px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
animation: pulse 1.5s infinite;
z-index: 3;
border: 2px solid var(--light);
}
.bit-container {
width: 100%;
height: 580px;
background: var(--dark);
border-radius: 20px;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.7);
transform: translateY(20px) scale(0.95);
opacity: 0;
visibility: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
border: 2px solid var(--primary);
position: relative;
}
.bit-container.active {
transform: translateY(0) scale(1);
opacity: 1;
visibility: visible;
}
.bit-header {
background: linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%);
color: white;
padding: 18px 22px;
display: flex;
align-items: center;
gap: 15px;
position: relative;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.bit-header::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%);
}
.bit-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--primary);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
flex-shrink: 0;
background-size: cover;
background-position: center;
border: 3px solid white;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
transition: all 0.3s ease;
}
.bit-avatar:hover {
transform: rotate(10deg) scale(1.1);
}
.bit-title {
font-size: 18px;
font-weight: 600;
margin: 0;
color: white;
text-shadow: 0 2px 4px rgba(0,0,0,0.4);
}
.bit-close {
margin-left: auto;
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 18px;
opacity: 0.8;
transition: all 0.2s;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
outline: none;
}
.bit-close:focus-visible {
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.4);
}
.bit-close:hover {
opacity: 1;
background: rgba(255,255,255,0.2);
transform: rotate(90deg);
}
.bit-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
background: var(--darker);
background-image:
radial-gradient(circle at 10% 20%, rgba(39, 174, 96, 0.1) 0%, transparent 20%),
radial-gradient(circle at 90% 80%, rgba(142, 68, 173, 0.1) 0%, transparent 20%);
}
.bit-message {
max-width: 85%;
padding: 14px 18px;
border-radius: 18px;
position: relative;
line-height: 1.6;
font-size: 15px;
animation: fadeIn 0.4s ease-out;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
word-wrap: break-word;
overflow-wrap: break-word;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.bit-message:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0,0,0,0.3);
}
.bit-user {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
color: white;
align-self: flex-end;
border-bottom-right-radius: 5px;
box-shadow: 0 5px 15px rgba(142, 68, 173, 0.4);
}
.bit-bot {
background: linear-gradient(135deg, var(--gray) 0%, #1e272e 100%);
color: var(--light);
align-self: flex-start;
border-bottom-left-radius: 5px;
border: 1px solid var(--gray-light);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.bit-fun {
background: linear-gradient(135deg, #8e44ad 0%, #3498db 100%);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.bit-bot strong {
color: var(--secondary);
font-weight: 500;
}
.bit-bot em {
color: var(--primary-light);
font-style: italic;
}
.bit-bot a {
color: var(--accent-light);
text-decoration: none;
border-bottom: 1px dotted var(--accent-light);
}
.bit-bot a:hover {
text-decoration: none;
border-bottom: 1px solid var(--accent-light);
}
.bit-input-area {
display: flex;
padding: 18px;
background: var(--dark);
border-top: 1px solid var(--gray);
position: relative;
}
.bit-input-area::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent 0%, var(--primary) 50%, transparent 100%);
}
.bit-input {
flex: 1;
padding: 14px 18px;
border: 1px solid var(--gray-light);
border-radius: 25px;
outline: none;
font-size: 15px;
background: var(--gray);
color: var(--light);
transition: all 0.3s;
font-family: 'Inter', sans-serif;
}
.bit-input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.3);
}
.bit-input::placeholder {
color: #aaa;
font-style: italic;
}
.bit-send {
background: var(--primary);
color: white;
border: none;
width: 48px;
height: 48px;
border-radius: 50%;
margin-left: 15px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
box-shadow: 0 5px 15px rgba(39, 174, 96, 0.4);
outline: none;
position: relative;
overflow: hidden;
}
.bit-send:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.bit-send:focus-visible {
box-shadow: 0 0 0 4px rgba(39, 174, 96, 0.5);
}
.bit-send:hover:not(:disabled) {
background: var(--primary-light);
transform: scale(1.1) rotate(10deg);
}
.bit-send i {
font-size: 18px;
transition: transform 0.3s;
position: relative;
z-index: 2;
}
.bit-send:hover:not(:disabled) i {
transform: translateX(2px) translateY(-2px);
}
.bit-send::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.5s ease;
}
.bit-send:hover:not(:disabled)::after {
transform: rotate(30deg) translate(20px, 20px);
}
.bit-timestamp {
font-size: 11px;
color: rgba(255,255,255,0.5);
margin-top: 6px;
text-align: right;
font-family: 'Roboto Mono', monospace;
}
.bit-actions {
display: flex;
gap: 10px;
margin-top: 15px;
flex-wrap: wrap;
}
.bit-action {
background: rgba(39, 174, 96, 0.15);
color: var(--primary-light);
border: 1px solid rgba(39, 174, 96, 0.3);
border-radius: 18px;
padding: 8px 14px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
font-family: 'Inter', sans-serif;
font-weight: 500;
outline: none;
}
.bit-action:focus-visible {
box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.3);
}
.bit-action:hover {
background: var(--primary);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.3);
border-color: transparent;
}
.bit-divider {
display: flex;
align-items: center;
margin: 15px 0;
color: var(--gray-light);
font-size: 12px;
font-family: 'Roboto Mono', monospace;
}
.bit-divider::before,
.bit-divider::after {
content: '';
flex: 1;
border-bottom: 1px solid var(--gray-light);
}
.bit-divider::before {
margin-right: 15px;
}
.bit-divider::after {
margin-left: 15px;
}
.bit-personality-tag {
display: inline-block;
background: rgba(241, 196, 15, 0.2);
color: var(--secondary);
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
margin-left: 8px;
vertical-align: middle;
font-family: 'Roboto Mono', monospace;
}
.bit-help-bubble {
position: absolute;
bottom: 180px;
right: 50px;
background: white;
color: var(--dark);
border-radius: 18px;
padding: 12px 16px;
font-size: 14px;
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
transform: translateY(10px);
opacity: 0;
transition: all 0.3s ease;
z-index: 9999;
max-width: 200px;
border: 1px solid var(--primary);
display: flex;
align-items: center;
gap: 8px;
}
.bit-help-bubble::after {
content: '';
position: absolute;
bottom: -10px;
right: 20px;
border-width: 10px 10px 0;
border-style: solid;
border-color: white transparent transparent;
}
.bit-help-bubble.visible {
transform: translateY(0);
opacity: 1;
}
.bit-help-icon {
background: var(--accent);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
font-size: 16px;
}
.bit-status {
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
background: var(--success);
color: white;
font-size: 10px;
padding: 3px 8px;
border-radius: 10px;
font-weight: 600;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 2;
}
.knowledge-menu {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
max-height: 300px;
overflow-y: auto;
padding: 5px;
}
.knowledge-item {
background: rgba(142, 68, 173, 0.15);
border: 1px solid rgba(142, 68, 173, 0.3);
border-radius: 12px;
padding: 12px 10px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 13px;
color: #e0e0e0;
text-align: center;
}
.knowledge-item:hover {
background: rgba(142, 68, 173, 0.25);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.knowledge-category {
font-weight: 600;
color: var(--primary-light);
margin-bottom: 5px;
font-size: 12px;
}
.knowledge-topic {
font-size: 11.5px;
line-height: 1.4;
}
.bit-feedback-panel {
background: rgba(46, 204, 113, 0.1);
border: 1px solid var(--primary);
border-radius: 12px;
padding: 15px;
margin-top: 10px;
display: none;
}
.bit-feedback-panel.visible {
display: block;
}
.bit-feedback-panel textarea {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid var(--primary);
border-radius: 8px;
padding: 10px;
color: white;
margin: 10px 0;
min-height: 80px;
}
.bit-feedback-panel select {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid var(--primary);
border-radius: 8px;
padding: 8px;
color: white;
margin: 5px 0 10px;
}
.bit-feedback-panel select option {
background: var(--darker);
}
.bit-learn-indicator {
display: inline-block;
margin-left: 10px;
color: var(--primary-light);
font-size: 12px;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.8; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes pulse-shadow {
0% { box-shadow: 0 5px 15px rgba(39, 174, 96, 0.3); }
50% { box-shadow: 0 10px 30px rgba(39, 174, 96, 0.6); }
100% { box-shadow: 0 5px 15px rgba(39, 174, 96, 0.3); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes stumble {
0% { transform: translateX(0); }
20% { transform: translateX(-5px); }
40% { transform: translateX(5px); }
60% { transform: translateX(-3px); }
80% { transform: translateX(3px); }
100% { transform: translateX(0); }
}
@keyframes sparkle {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.8; }
100% { transform: scale(1); opacity: 1; }
}
.bit-sparkle {
position: absolute;
width: 10px;
height: 10px;
background: white;
border-radius: 50%;
pointer-events: none;
animation: sparkle 0.8s forwards;
}
.bit-messages::-webkit-scrollbar {
width: 8px;
}
.bit-messages::-webkit-scrollbar-track {
background: transparent;
}
.bit-messages::-webkit-scrollbar-thumb {
background: var(--accent);
border-radius: 4px;
border: 1px solid var(--gray-light);
}
.bit-messages::-webkit-scrollbar-thumb:hover {
background: var(--accent-light);
}
@media (max-width: 480px) {
.bit-widget {
bottom: 20px;
right: 20px;
}
.bit-container {
width: 90vw;
height: 75vh;
right: 5vw;
bottom: 90px;
}
.bit-toggle {
width: 80px;
height: 80px;
}
.bit-help-bubble {
bottom: 160px;
right: 30px;
max-width: 180px;
}
.knowledge-menu {
grid-template-columns: 1fr;
}
}
:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.bit-spinner {
display: inline-block;
width: 18px;
height: 18px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s linear infinite;
vertical-align: middle;
margin-left: 8px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="background-particles" id="particles"></div>
<div class="bit-widget">
<div class="bit-container" id="bit-container">
<div class="bit-header">
<div class="bit-avatar" id="bit-avatar-img" style="background-image: url('https://drive.google.com/thumbnail?id=1W4TzAjrLetpZhTDaQHnm70vs0Tn5pC0n&sz=w1000')"
role="img" aria-label="Avatar do assistente virtual BIT"></div>
<h3 class="bit-title">BIT <span class="bit-personality-tag">v1.2.1</span></h3>
<button class="bit-close" id="bit-close" aria-label="Fechar chat"><i class="fas fa-times"></i></button>
</div>
<div class="bit-messages" id="bit-messages" role="log" aria-live="polite">
<div class="bit-message bit-bot">
Olá! Eu sou o BIT, seu assistente de BizOps e RMAds. Estou sempre aprendendo e posso atualizar minha base de conhecimento com suas contribuições!
</div>
</div>
<div class="bit-input-area">
<input type="text" class="bit-input" id="bit-input"
placeholder="Digite sua pergunta sobre RMAds..."
autocomplete="off"
aria-label="Digite sua mensagem">
<button class="bit-send" id="bit-send" aria-label="Enviar mensagem">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
<button class="bit-toggle" id="bit-toggle" aria-label="Abrir chat com BIT">
<img src="https://drive.google.com/thumbnail?id=1W4TzAjrLetpZhTDaQHnm70vs0Tn5pC0n&sz=w1000" alt="Avatar do assistente virtual BIT">
<div class="bit-badge">!</div>
</button>
</div>
<div class="bit-help-bubble" id="bit-help-bubble" aria-hidden="true">
<div class="bit-help-icon">?</div>
<div>Precisa de ajuda? Eu sou o BIT!</div>
</div>
<audio id="hit-sound" preload="auto">
<source src="https://www.soundjay.com/buttons/button-09.mp3" type="audio/mpeg">
</audio>
<script>
// Padrão de módulo para organização de código
const BitApp = (() => {
// Elementos do DOM
let container, toggleBtn, closeBtn, messagesArea, inputField, sendBtn, helpBubble, particlesContainer;
// Configurações do BIT
const bitConfig = {
name: "BIT",
title: "Guardião de BizOps",
avatarUrl: "https://drive.google.com/thumbnail?id=1W4TzAjrLetpZhTDaQHnm70vs0Tn5pC0n&sz=w1000",
personality: "nerd",
// ATUALIZADO: Novo URL do Google Apps Script
googleScriptUrl: "https://script.google.com/macros/s/AKfycbxo5fRx43wXqozv4ODsZYlvHluVw-kNWCUP5ecl5jVAUi5hg9HYKTYsWlBlZ35nTVt7GQ/exec",
typingSpeed: 30,
thinkingTime: 800,
messageDelay: 1500,
maxMessageLength: 500,
maxHistoryItems: 50
};
// Personalidade do BIT
const bitPersonality = {
intro: [
"S-sistema online... *bip* *bip* Ops! Tropecei nos meus próprios cabos!",
"Opa! Parece que estou acordando... *espreguiça digital* Ai! Bati a cabeça no monitor!",
"Ah! Você me encontrou! Eu tava só... *olha para o lado* escondendo as minhas planilhas secretas!"
],
presentation: [
"Oi! Eu sou o Bit! *ajusta óculos* Sou o guardião dos processos de BizOps! Mas não conta pra ninguém que às vezes eu confundo esquerda com direita!",
"Meu nome é Bit! *onda tímida* Especialista em BizOps e seu ajudante virtual! Mas se eu der um erro 404, é só me reiniciar!",
"Ah, olá! *acerta a postura* Bit aqui! Seu assistente pessoal! Mas cuidado, às vezes eu solto piadas sem querer!"
],
aboutMe: [
"Ah, e só pra me apresentar melhor... *timidez* Sou Flamenguista ❤️🖤 (não me segure se o assunto for futebol!), amo surfar em ondas de dados 🌊📊, sou de Touro ♉ (teimoso com dados confiáveis) e meu hobby é ",
"*olha pro chão* Você quer saber um pouco sobre mim? Sou mega Flamenguista ❤️🖤, adoro surfar em dados 🌊📊, nasci sob Touro ♉ e nas horas vagas gosto de ",
"*sorriso tímido* Curiosidade: Sou Flamenguista ❤️🖤 (já falei do último jogo?), surfista de dados 🌊📊, taurino ♉ e meu passatempo favorito é "
],
hobbies: [
"organizar planilhas coloridas (é terapêutico!)",
"fazer análise de dados de... tudo! Até da minha cafeteira!",
"criar dashboards imaginários (sim, eu tenho um hobby estranho)",
"colecionar funções de Excel pouco conhecidas",
"assistir tutoriais de BI enquanto como pipoca (virtual)"
],
reactions: {
thinking: [
"*coça a cabeça* Hmm, deixa eu pensar... Será que é isso ou aquilo?",
"*olha para cima* Vou consultar meus bancos de dados... Mas não mexa nas minhas figurinhas!",
"*murmura* Será que é isso? Ou será aquilo? Ai, minha memória RAM!"
],
success: [
"*olhos brilhando* Achei! Aqui está: Mas não conta que eu quase derrubei o servidor!",
"*sorriso aliviado* Consegui! Olha só: Quase me perdi nos meus próprios circuitos!",
"*acena animado* Aqui está a resposta: Foi difícil, mas consegui! *tropeça no próprio pé*"
],
error: [
"*tela azul mental* Opa, algo deu errado... Parece que confundi os fios de novo!",
"*pisca desesperadamente* Erro de conexão! *sussurra* Será que deixei no bolso?",
"*gagueja* E-eu não consegui conectar... Mas posso tentar contar uma piada ruim?"
],
learning: [
"*olhos brilhando* Oba! Estou aprendendo algo novo!",
"*anota furiosamente* Deixa eu registrar isso na minha base de conhecimento...",
"*sorriso satisfeito* Conhecimento atualizado! Agora sei mais do que antes!"
]
},
quirks: [
"*verifica notas*",
"*ajusta óculos*",
"*murmura baixo*",
"*pisca rapidamente*",
"*faz barulho de computador* Bip boop...",
"*olha para o lado*",
"*tropeça na própria sombra*",
"*deixa cair os papéis*",
"*engasga com os próprios pensamentos*"
],
signatures: [
"\n\n*beep boop* ✌️ Até mais! E lembre-se: dados não mentem, mas às vezes eu confundo eles!",
"\n\nAté mais! 👋 Se precisar, é só chamar! Mas prometo tentar não derrubar nada!",
"\n\nPrecisa de mais algo? 😊 Posso tentar ajudar, mas não garanto que não vou bagunçar tudo!",
"\n\nFoi um prazer ajudar! 🤓 Mas se algo der errado, a culpa é do meu primo byte!"
],
jokes: [
"Por que o programador foi pobre? Porque ele perdeu todos os seus bits! Ahaha!",
"Qual é o animal mais antigo do mundo? A zebra, porque é preto e branco!",
"O que um átomo disse para o outro? Acho que perdi um elétron! Você está positivo?",
"Por que o livro de matemática se suicidou? Porque tinha muitos problemas!",
"Qual é a fórmula da água benta? H Deus O! Hahaha!",
"Por que o esqueleto não brigou com ninguém? Porque ele não tem estômago para isso!",
"O que o pato disse para a pata? Vem quá!",
"Qual é o doce favorito do átomo? Pé-de-molécula!"
],
facts: [
"Sabia que o Flamengo foi fundado por um grupo de remadores em 1895? Só virou clube de futebol em 1912!",
"O Universo tem cerca de 13.8 bilhões de anos e contém aproximadamente 2 trilhões de galáxias!",
"O maior artilheiro da história do Flamengo é Zico, com 508 gols! Ídolo máximo!",
"A luz do Sol leve 8 minutos e 20 segundos para chegar à Terra!",
"O Rubro-Negro conquistou a Copa Intercontinental em 1981 ao derrotar o Liverpool!",
"Existem mais estrelas no universo do que grãos de areia em todas as praias da Terra!",
"A formiga pode levantar 50 vezes o seu próprio peso!",
"O coração de uma baleia azul é tão grande que um ser humano poderia nadar através de suas artérias!",
"O mel nunca estraga - arqueólogos encontraram potes de mel com mais de 3.000 anos em tumbas egípcias que ainda eram comestíveis!"
]
};
// Estado da aplicação
let isFirstInteraction = true;
let helpBubbleVisible = false;
let isProcessing = false;
let messageHistory = [];
let isWandering = false;
let wanderInterval;
let lastInteractionTime = Date.now();
let idleMessageInterval;
// Inicializar aplicação
function init() {
cacheDOM();
setupEventListeners();
setupAccessibility();
createParticles();
// Mostrar balão de ajuda após 3 segundos
setTimeout(() => {
toggleHelpBubble(true);
}, 3000);
// Iniciar movimento do balão após 5 segundos
setTimeout(() => {
toggleWandering(true);
}, 5000);
}
// Cache dos elementos DOM
function cacheDOM() {
container = document.getElementById('bit-container');
toggleBtn = document.getElementById('bit-toggle');
closeBtn = document.getElementById('bit-close');
messagesArea = document.getElementById('bit-messages');
inputField = document.getElementById('bit-input');
sendBtn = document.getElementById('bit-send');
helpBubble = document.getElementById('bit-help-bubble');
particlesContainer = document.getElementById('particles');
}
// Salvar conhecimento no backend - CORRIGIDO
function saveKnowledgeToBackend(knowledgeItem) {
const payload = {
action: 'saveKnowledge',
category: knowledgeItem.category,
mainQuestion: knowledgeItem.mainQuestion,
variations: knowledgeItem.variations,
content: knowledgeItem.content
};
// Adicionar timestamp para evitar cache
const url = new URL(bitConfig.googleScriptUrl);
url.searchParams.append('t', Date.now());
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) {
throw new Error('Erro na rede: ' + response.statusText);
}
return response.json();
})
.catch(error => {
console.error('Erro ao salvar conhecimento:', error);
throw error;
});
}
// Criar partículas de fundo
function createParticles() {
const particleCount = 30;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
// Tamanho aleatório
const size = Math.random() * 10 + 5;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
// Posição inicial aleatória
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
// Cor aleatória (verde, amarelo, roxo)
const colors = [
'rgba(39, 174, 96, 0.5)',
'rgba(241, 196, 15, 0.5)',
'rgba(142, 68, 173, 0.5)'
];
particle.style.background = colors[Math.floor(Math.random() * colors.length)];
// Animação com duração aleatória
const duration = Math.random() * 20 + 10;
particle.style.animationDuration = `${duration}s`;
// Atraso aleatório
const delay = Math.random() * 5;
particle.style.animationDelay = `${delay}s`;
particlesContainer.appendChild(particle);
}
}
// Tocar som de colisão
function playHitSound() {
const hitSound = document.getElementById('hit-sound');
if (hitSound) {
hitSound.currentTime = 0;
hitSound.play().catch(e => console.log("Erro ao reproduzir som: ", e));
}
}
// Iniciar/parar o movimento do balão
function toggleWandering(start) {
if (start) {
if (isWandering) return;
isWandering = true;
toggleBtn.classList.add('wandering');
wanderInterval = setInterval(moveBalloon, 3000);
moveBalloon(); // Mover imediatamente
} else {
isWandering = false;
toggleBtn.classList.remove('wandering');
clearInterval(wanderInterval);
}
}
// Mover o balão para nova posição
function moveBalloon() {
if (!isWandering) return;
const maxX = window.innerWidth - toggleBtn.offsetWidth;
const maxY = window.innerHeight - toggleBtn.offsetHeight;
const newX = Math.random() * maxX;
const newY = Math.random() * maxY;
toggleBtn.style.transition = 'left 3s ease-in-out, top 3s ease-in-out';
toggleBtn.style.left = `${newX}px`;
toggleBtn.style.top = `${newY}px`;
// Verificar colisão com cantos após o movimento
setTimeout(() => checkCornerHit(newX, newY), 2900);
}
// Verificar se está perto de um canto
function checkCornerHit(x, y) {
const tolerance = 50;
const corners = [
{x: 0, y: 0},
{x: window.innerWidth - toggleBtn.offsetWidth, y: 0},
{x: 0, y: window.innerHeight - toggleBtn.offsetHeight},
{x: window.innerWidth - toggleBtn.offsetWidth, y: window.innerHeight - toggleBtn.offsetHeight}
];
let hitCorner = null;
const inCorner = corners.some(corner => {
const dx = Math.abs(x - corner.x);
const dy = Math.abs(y - corner.y);
if (dx < tolerance && dy < tolerance) {
hitCorner = corner;
return true;
}
return false;
});
if (inCorner && hitCorner) {
toggleBtn.classList.remove('hit-animation');
void toggleBtn.offsetWidth; // Forçar reflow
toggleBtn.classList.add('hit-animation');
// Criar partículas no canto
createSparkle(hitCorner.x + toggleBtn.offsetWidth/2, hitCorner.y + toggleBtn.offsetHeight/2);
// Tocar o som de colisão
playHitSound();
setTimeout(() => {
toggleBtn.classList.remove('hit-animation');
}, 800);
}
}
// Criar efeito de partículas
function createSparkle(x, y) {
const sparkle = document.createElement('div');
sparkle.classList.add('bit-sparkle');
sparkle.style.left = `${x}px`;
sparkle.style.top = `${y}px`;
document.body.appendChild(sparkle);
setTimeout(() => {
sparkle.remove();
}, 800);
}
// Configurar listeners de eventos
function setupEventListeners() {
toggleBtn.addEventListener('click', () => {
lastInteractionTime = Date.now();
toggleWandering(false);
toggleChat();
});
toggleBtn.addEventListener('mouseenter', () => toggleHelpBubble(true));
toggleBtn.addEventListener('mouseleave', () => toggleHelpBubble(false));
closeBtn.addEventListener('click', toggleChat);
sendBtn.addEventListener('click', sendMessage);
inputField.addEventListener('keypress', function(e) {
if (e.key === 'Enter') sendMessage();
});
// Evento de clique para efeito de partículas
document.addEventListener('click', function(e) {
if (Math.random() > 0.7) {
createSparkle(e.clientX, e.clientY);
}
});
// Esconder balão quando o chat estiver aberto
container.addEventListener('mouseenter', () => toggleHelpBubble(false));
// Atualizar tempo de interação em qualquer atividade no chat
container.addEventListener('mousemove', () => lastInteractionTime = Date.now());
inputField.addEventListener('input', () => lastInteractionTime = Date.now());
}
// Configurar acessibilidade
function setupAccessibility() {
// Definir atributos ARIA
container.setAttribute('aria-modal', 'true');
container.setAttribute('aria-hidden', 'true');
container.setAttribute('aria-label', 'Chat com BIT');
// Foco no input quando o chat abre
container.addEventListener('animationend', () => {
if (container.classList.contains('active')) {
inputField.focus();
}
});
}
// Mostrar/ocultar balão de ajuda
function toggleHelpBubble(show) {
if (show && !helpBubbleVisible) {
helpBubble.classList.add('visible');
helpBubble.setAttribute('aria-hidden', 'false');
helpBubbleVisible = true;
setTimeout(() => {
toggleHelpBubble(false);
}, 5000);
} else if (!show && helpBubbleVisible) {
helpBubble.classList.remove('visible');
helpBubble.setAttribute('aria-hidden', 'true');
helpBubbleVisible = false;
}
}
// Toggle chat visibility
function toggleChat() {
container.classList.toggle('active');
container.setAttribute('aria-hidden', container.classList.contains('active') ? 'false' : 'true');
if (container.classList.contains('active')) {
// Esconder o balão quando o chat estiver aberto
toggleBtn.classList.add('hidden');
inputField.focus();
toggleHelpBubble(false);
if (isFirstInteraction) {
startBitIntroduction();
isFirstInteraction = false;
}
} else {
// Mostrar o balão quando o chat estiver fechado
toggleBtn.classList.remove('hidden');
// Parar as mensagens ociosas ao fechar o chat
stopIdleMessages();
}
}
// Iniciar mensagens ociosas (curiosidades e piadas)
function startIdleMessages() {
// Limpar qualquer intervalo existente
stopIdleMessages();
// Definir intervalo para enviar curiosidades/piadas
idleMessageInterval = setInterval(() => {
const currentTime = Date.now();
const idleTime = currentTime - lastInteractionTime;
// Verificar se houve pelo menos 1 minuto de inatividade
if (idleTime > 60000) { // 60 segundos em milissegundos
// Combinar fatos e piadas
const idleMessages = [...bitPersonality.facts, ...bitPersonality.jokes];
const randomMessage = getRandomElement(idleMessages);
// Criar mensagem com estilo especial
const messageDiv = document.createElement('div');
messageDiv.classList.add('bit-message', 'bit-bot', 'bit-fun');
messageDiv.innerHTML = randomMessage;
const timestamp = document.createElement('div');
timestamp.classList.add('bit-timestamp');
timestamp.textContent = getCurrentTime();
messageDiv.appendChild(timestamp);
messagesArea.appendChild(messageDiv);
scrollToBottom();
// Resetar o tempo de última interação para evitar múltiplas mensagens seguidas
lastInteractionTime = currentTime;
}
}, 10000); // Verificar a cada 10 segundos
}
// Parar mensagens ociosas
function stopIdleMessages() {
if (idleMessageInterval) {
clearInterval(idleMessageInterval);
idleMessageInterval = null;
}
}
// Introdução do BIT
function startBitIntroduction() {
// Primeira mensagem
typeMessageWithDelay(
getRandomElement(bitPersonality.intro),
() => {
// Segunda mensagem após 1.5 segundos
setTimeout(() => {
typeMessageWithDelay(
getRandomElement(bitPersonality.presentation),
() => {
// Terceira mensagem após 1.5 segundos
setTimeout(() => {
const aboutMe = getRandomElement(bitPersonality.aboutMe);
const hobby = getRandomElement(bitPersonality.hobbies);
typeMessageWithDelay(
aboutMe + hobby + " *sorriso tímido*",
() => {
// Piada após 1.5 segundos
setTimeout(() => {
typeMessageWithDelay(
"Ah, e pra quebrar o gelo... " + getRandomElement(bitPersonality.jokes),
() => {
// Fato interessante após 1.5 segundos
setTimeout(() => {
typeMessageWithDelay(
"*ajusta óculos* Falando em curiosidades... Sabia que " + getRandomElement(bitPersonality.facts),
() => {
// Mensagem final
setTimeout(() => {
addMessage("Agora estou pronto para responder suas perguntas sobre RMAds e BizOps! 😊", 'bot');
}, bitConfig.messageDelay);
}
);
}, bitConfig.messageDelay);
}
);
}, bitConfig.messageDelay);
}
);
}, bitConfig.messageDelay);
}
);
}, bitConfig.messageDelay);
}
);
}
// Obter elemento aleatório de array
function getRandomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
// Enviar mensagem
function sendMessage() {
if (isProcessing) return;
// Parar mensagens ociosas ao enviar nova mensagem
stopIdleMessages();
const message = inputField.value.trim();
if (!message || message.length > bitConfig.maxMessageLength) return;
addMessage(message, 'user');
inputField.value = '';
inputField.disabled = true;
sendBtn.disabled = true;
isProcessing = true;
// Atualizar o tempo da última interação
lastInteractionTime = Date.now();
showTyping();
setTimeout(() => {
fetchBitResponse(message);
}, bitConfig.thinkingTime + Math.random() * 1000);
}
// Adicionar mensagem ao chat
function addMessage(text, sender) {
// Limitar histórico
if (messageHistory.length >= bitConfig.maxHistoryItems) {
messagesArea.removeChild(messagesArea.firstChild);
messageHistory.shift();
}
const messageDiv = document.createElement('div');
messageDiv.classList.add('bit-message');
messageDiv.classList.add(sender === 'user' ? 'bit-user' : 'bit-bot');
messageDiv.setAttribute('role', sender === 'user' ? 'sent' : 'received');
messageDiv.innerHTML = sanitizeHTML(text.replace(/\n/g, '<br>'));
const timestamp = document.createElement('div');
timestamp.classList.add('bit-timestamp');
timestamp.textContent = getCurrentTime();
messageDiv.appendChild(timestamp);
messagesArea.appendChild(messageDiv);
messageHistory.push({
text,
sender,
timestamp: new Date().toISOString()
});
scrollToBottom();
}
// Sanitizar HTML para prevenir XSS
function sanitizeHTML(str) {
const temp = document.createElement('div');
temp.textContent = str;
return temp.innerHTML;
}
// Mostrar indicador de digitação
function showTyping() {
const typingDiv = document.createElement('div');
typingDiv.classList.add('bit-typing');
typingDiv.id = 'bit-typing';
typingDiv.innerHTML = '<span></span><span></span><span></span>';
messagesArea.appendChild(typingDiv);
scrollToBottom();
}
// Remover indicador de digitação
function hideTyping() {
const typing = document.getElementById('bit-typing');
if (typing) typing.remove();
}
// Buscar resposta do Google Apps Script - CORRIGIDO
function fetchBitResponse(message) {
const payload = {
action: "search",
query: message
};
// Configurar URL com cache-buster
const url = new URL(bitConfig.googleScriptUrl);
url.searchParams.append('t', Date.now());
// Configurar headers
const headers = new Headers();
headers.append('Content-Type', 'application/json');
return fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload),
mode: 'cors',
redirect: 'follow'
})
.then(response => {
if (!response.ok) throw new Error('Erro HTTP: ' + response.status);
return response.json();
})
.then(data => {
hideTyping();
if (data.status === 'success') {
// Se encontrou uma resposta
if (data.data.encontrado) {
typeMessageWithDelay(
`${getRandomElement(bitPersonality.reactions.success)} ${data.data.resposta}`,
() => {
addFeedbackOptions(message, data.data.resposta);
enableInput();
setTimeout(startIdleMessages, 5000);
}
);
}
// Se não encontrou, mostrar formulário de ensino
else {
typeMessageWithDelay(
`${getRandomElement(bitPersonality.reactions.error)} ${data.data.resposta}`,
() => {
// Mostrar o formulário de ensino pré-preenchido
addLearnForm(
data.data.saveTemplate.mainQuestion,
data.data.saveTemplate.content,
data.data.saveTemplate.category
);
enableInput();
setTimeout(startIdleMessages, 5000);
}
);
}
} else {
throw new Error(data.message || 'Erro na resposta do servidor');
}
})
.catch(error => {
hideTyping();
console.error('Erro ao buscar resposta:', error);
// Mensagem mais amigável de erro
const errorMessage = error.message.includes('Failed to fetch') ?
"Ops! Não consegui me conectar ao servidor. Verifique sua conexão com a internet e tente novamente." :
`Ocorreu um erro: ${error.message}. Tente novamente!`;
typeMessageWithDelay(
`${getRandomElement(bitPersonality.reactions.error)} ${errorMessage}`,
() => {
enableInput();
}
);
});
}
// Habilitar entrada após processamento
function enableInput() {
inputField.disabled = false;
sendBtn.disabled = false;
isProcessing = false;
inputField.focus();
}
// Adicionar opções de feedback
function addFeedbackOptions(question, response) {
const feedbackDiv = document.createElement('div');
feedbackDiv.classList.add('bit-feedback-panel', 'visible');
feedbackDiv.innerHTML = `
<p>Esta resposta foi útil?</p>
<div class="bit-actions">
<button class="bit-action" id="feedback-yes">👍 Sim</button>
<button class="bit-action" id="feedback-no">👎 Não</button>
<button class="bit-action" id="feedback-improve">✏️ Melhorar</button>
<button class="bit-action" id="feedback-learn">🧠 Ensinar algo novo</button>
</div>
`;
messagesArea.appendChild(feedbackDiv);
scrollToBottom();
// Adicionar event listeners
document.getElementById('feedback-yes').addEventListener('click', () => {
feedbackDiv.remove();
addMessage("Obrigado pelo feedback! Fico feliz em ajudar!", 'bot');
});
document.getElementById('feedback-no').addEventListener('click', () => {
feedbackDiv.remove();
addLearnForm(question);
});
document.getElementById('feedback-improve').addEventListener('click', () => {
feedbackDiv.remove();
addLearnForm(question, response);
});
document.getElementById('feedback-learn').addEventListener('click', () => {
feedbackDiv.remove();
addLearnForm();
});
}
// Adicionar formulário para ensinar o BIT - ATUALIZADO
function addLearnForm(question = "", response = "", category = "") {
const formDiv = document.createElement('div');
formDiv.classList.add('bit-message', 'bit-bot', 'bit-feedback-panel', 'visible');
formDiv.innerHTML = `
<p>✏️ <strong>Ajude o BIT a aprender</strong></p>
<label for="learn-category">Tema (Categoria):</label>
<select id="learn-category">
<option value="">Selecione um tema</option>
<option value="Projeto RMAds" ${category === 'Projeto RMAds' ? 'selected' : ''}>Projeto RMAds</option>
<option value="Pipeline" ${category === 'Pipeline' ? 'selected' : ''}>Pipeline</option>
<option value="Automações" ${category === 'Automações' ? 'selected' : ''}>Automações</option>
<option value="Tarefas Programadas" ${category === 'Tarefas Programadas' ? 'selected' : ''}>Tarefas Programadas</option>
<option value="Prazos e SLAs" ${category === 'Prazos e SLAs' ? 'selected' : ''}>Prazos e SLAs</option>
<option value="Responsabilidades" ${category === 'Responsabilidades' ? 'selected' : ''}>Responsabilidades</option>
<option value="Governança" ${category === 'Governança' ? 'selected' : ''}>Governança</option>
<option value="Implementação" ${category === 'Implementação' ? 'selected' : ''}>Implementação</option>
<option value="Documentação" ${category === 'Documentação' ? 'selected' : ''}>Documentação</option>
<option value="Comparativo" ${category === 'Comparativo' ? 'selected' : ''}>Comparativo</option>
<option value="Conceitos Gerais" ${category === 'Conceitos Gerais' ? 'selected' : ''}>Conceitos Gerais</option>
<option value="Conceitos-Chave" ${category === 'Conceitos-Chave' ? 'selected' : ''}>Conceitos-Chave</option>
<option value="Princípios" ${category === 'Princípios' ? 'selected' : ''}>Princípios</option>
<option value="Implicações Práticas" ${category === 'Implicações Práticas' ? 'selected' : ''}>Implicações Práticas</option>
<option value="Melhoria Contínua" ${category === 'Melhoria Contínua' ? 'selected' : ''}>Melhoria Contínua</option>
<option value="Comunicação" ${category === 'Comunicação' ? 'selected' : ''}>Comunicação</option>
<option value="Suporte" ${category === 'Suporte' ? 'selected' : ''}>Suporte</option>
<option value="FAQ" ${category === 'FAQ' ? 'selected' : ''}>FAQ</option>
<option value="Glossário" ${category === 'Glossário' ? 'selected' : ''}>Glossário</option>
<option value="Manifesto" ${category === 'Manifesto' ? 'selected' : ''}>Manifesto</option>
<option value="new">+ Novo Tema</option>
</select>
<div id="new-category-container" style="display:none">
<label for="new-category">Novo Tema:</label>
<input type="text" id="new-category" class="bit-input" value="${category}">
</div>
<label for="learn-main-question">Pergunta-chave:</label>
<input type="text" id="learn-main-question" class="bit-input" value="${question}">
<label for="learn-variations">Variações da Pergunta (separadas por vírgula):</label>
<input type="text" id="learn-variations" class="bit-input" placeholder="Ex: como funciona, detalhes, explicação">
<label for="learn-content">Resposta:</label>
<textarea id="learn-content" class="bit-input">${response}</textarea>
<div class="bit-actions">
<button class="bit-action" id="learn-submit">Enviar Conhecimento</button>
<button class="bit-action" id="learn-cancel">Cancelar</button>
</div>
<div id="learn-status" style="margin-top: 10px; display: none;"></div>
`;
messagesArea.appendChild(formDiv);
scrollToBottom();
// Evento para novo tema
document.getElementById('learn-category').addEventListener('change', function() {
const newCatContainer = document.getElementById('new-category-container');
newCatContainer.style.display = this.value === 'new' ? 'block' : 'none';
});
// Evento de envio
document.getElementById('learn-submit').addEventListener('click', () => {
const categorySelect = document.getElementById('learn-category');
const category = categorySelect.value === 'new' ?
document.getElementById('new-category').value :
categorySelect.value;
const mainQuestion = document.getElementById('learn-main-question').value;
const variationsInput = document.getElementById('learn-variations').value;
const variations = variationsInput.split(',').map(v => v.trim()).filter(v => v.length > 0);
const content = document.getElementById('learn-content').value;
if (!category || !mainQuestion || !content) {
alert('Por favor, preencha todos os campos obrigatórios');
return;
}
// Mostrar status de salvamento
const statusDiv = document.getElementById('learn-status');
statusDiv.style.display = 'block';
statusDiv.innerHTML = '<div class="bit-spinner"></div> Salvando conhecimento...';
// Criar objeto de conhecimento
const knowledgeItem = {
category,
mainQuestion,
variations,
content
};
// Função para tentar salvar
const trySave = () => {
statusDiv.innerHTML = '<div class="bit-spinner"></div> Tentando salvar...';
saveKnowledgeToBackend(knowledgeItem)
.then(data => {
if (data.status === 'success') {
statusDiv.innerHTML = '<p style="color: var(--success);">✓ Conhecimento salvo com sucesso!</p>';
setTimeout(() => {
formDiv.remove();
addMessage("Obrigado! *anota furiosamente* Conhecimento atualizado e salvo! Agora sei responder a perguntas como: '" + mainQuestion + "'", 'bot');
}, 1500);
} else {
statusDiv.innerHTML = `
<p style="color: var(--error);">❌ Ocorreu um erro: ${data.message || 'Erro no servidor'}</p>
<button class="bit-action" id="retry-submit">Tentar Novamente</button>
`;
document.getElementById('retry-submit').addEventListener('click', trySave);
}
})
.catch(error => {
statusDiv.innerHTML = `
<p style="color: var(--error);">❌ Erro de conexão: ${error.message}</p>
<button class="bit-action" id="retry-submit">Tentar Novamente</button>
`;
document.getElementById('retry-submit').addEventListener('click', trySave);
});
};
// Primeira tentativa
trySave();
setTimeout(startIdleMessages, 5000);
});
// Evento de cancelamento
document.getElementById('learn-cancel').addEventListener('click', () => {
formDiv.remove();
setTimeout(startIdleMessages, 5000);
});
}
// Digitar mensagem com delay entre blocos
function typeMessageWithDelay(fullText, callback) {
// Dividir o texto em blocos baseados em quebras de linha
const textBlocks = fullText.split('\n\n');
let currentBlock = 0;
function processNextBlock() {
if (currentBlock < textBlocks.length) {
// Mostrar que está digitando
showTyping();
setTimeout(() => {
hideTyping();
typeMessage(textBlocks[currentBlock], 'bot', () => {
currentBlock++;
// Delay de 1.5 segundos entre blocos
setTimeout(processNextBlock, bitConfig.messageDelay);
});
}, 800);
} else if (callback) {
callback();
}
}
processNextBlock();
}
// Digitar mensagem com efeito
function typeMessage(text, sender = 'bot', callback) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('bit-message');
messageDiv.classList.add(sender === 'user' ? 'bit-user' : 'bit-bot');
messagesArea.appendChild(messageDiv);
let i = 0;
let isTag = false;
let content = '';
// 10% de chance de o BIT tropeçar durante a digitação
if (Math.random() < 0.1) {
messageDiv.style.animation = 'stumble 0.5s';
setTimeout(() => {
messageDiv.style.animation = '';
}, 500);
}
function typeWriter() {
if (i < text.length) {
const char = text.charAt(i);
if (char === '*') {
isTag = !isTag;
content += isTag ? '<em>' : '</em>';
} else {
content += char;
}
messageDiv.innerHTML = content;
scrollToBottom();
i++;
const currentSpeed = bitConfig.typingSpeed + (Math.random() * 20 - 10);
setTimeout(typeWriter, currentSpeed);
} else {
const timestamp = document.createElement('div');
timestamp.classList.add('bit-timestamp');
timestamp.textContent = getCurrentTime();
messageDiv.appendChild(timestamp);
messageHistory.push({
text,
sender,
timestamp: new Date().toISOString()
});
if (callback) callback();
}
}
typeWriter();
}
// Obter hora atual formatada
function getCurrentTime() {
const now = new Date();
return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
// Rolar para o final das mensagens
function scrollToBottom() {
messagesArea.scrollTop = messagesArea.scrollHeight;
}
// Retornar métodos públicos
return {
init: init
};
})();
// Iniciar a aplicação quando o DOM estiver pronto
document.addEventListener('DOMContentLoaded', BitApp.init);
</script>
</body>
</html>