Justificació de l’Arquitectura
Per muntar aquest projecte hem separat l’aplicació en tres parts que es connecten entre si: el Widget (FrontEnd), el Servidor Flask (BackEnd) i el fitxer de dades (JSON).

Per què separem el FrontEnd del BackEnd?
El FrontEnd és només el disseny, el que veu l’usuari a la pantalla. En canvi, el BackEnd és com el cervell del sistema.
Si poséssim la contrasenya o clau de la API de Gemini directa al codi de la web, qualsevol podria fer clic dret, donar-li a «Inspeccionar» i veure la nostra clau. Com que ho posem a la part del servidor en Python, ningú la pot veure des de fora i protegim les dades
El fitxer JSON i el programa ngrok
Arxiu JSON: Funciona com una base de dades local molt senzilla. Allà guardem el text amb les dades de la LAN Party EcoTech 2026 (els horaris, on es fa, les normes) perquè el xatbot les llegeixi i sàpiga què respondre, evitant que inventi coses falses.
ngrok: Com que el servidor Flask s’executa dins de Google Colab, la seva adreça IP és privada i local. Fem servir ngrok per obrir un túnel segur cap a internet (HTTPS). Així, la nostra pàgina web es pot connectar al servidor des de qualsevol ordinador amb internet.
Evitar errors perquè el programa no es pengi
Hem posat línies de codi per controlar que el programa no es tanqui si falla alguna cosa:
| Si falta el fitxer JSON | Error de saturació |
Fem servir un bloc try-except. Si el fitxer informacio.json s’esborra per error, el servidor no peta; salta directament a carregar un missatge de text que hem deixat per defecte. | Si enviem molts missatges seguits i el servidor de Google es col·lapsa per la quota, el codi reinicia la sessió del xat automàticament i avisa l’usuari amb un missatge a la pantalla, sense que s’hagi de reiniciar tot el programa a mà. |
Creació del Widget de la Web
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EcoTech 2026 - Asistent Virtual</title>
<style>
:root {
--primary: #2ecc71; /* Verd Eco */
--dark: #1a1a1a;
--light: #f4f4f4;
}
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #121212; }
/* Contenidor del Chat */
#chat-widget {
position: fixed; bottom: 20px; right: 20px;
width: 350px; height: 500px;
background: var(--dark); border: 2px solid var(--primary);
border-radius: 10px; display: flex; flex-direction: column;
box-shadow: 0 10px 25px rgba(0,0,0,0.5); overflow: hidden;
}
/* Capçalera */
#chat-header {
background: var(--primary); color: black; padding: 15px;
font-weight: bold; text-align: center; font-size: 1.1em;
}
/* Àrea de missatges */
#chat-content {
flex: 1; padding: 15px; overflow-y: auto;
display: flex; flex-direction: column; gap: 10px;
background-image: linear-gradient(rgba(46, 204, 113, 0.05) 1px, transparent 1px);
background-size: 20px 20px;
}
.message { padding: 10px; border-radius: 8px; max-width: 80%; font-size: 0.9em; }
.user { align-self: flex-end; background: var(--primary); color: black; }
.bot { align-self: flex-start; background: #333; color: white; border: 1px solid #444; }
/* Controls */
#chat-controls { padding: 10px; display: flex; gap: 5px; background: #222; }
input { flex: 1; padding: 10px; border: none; border-radius: 5px; outline: none; }
button {
padding: 10px; border: none; border-radius: 5px; cursor: pointer;
font-weight: bold; transition: 0.3s;
}
#btn-send { background: var(--primary); color: black; }
#btn-clear { background: #e74c3c; color: white; }
button:hover { opacity: 0.8; }
</style>
</head>
<body>
<div id="chat-widget">
<div id="chat-header">🤖 EcoTech Assistant</div>
<div id="chat-content">
<div class="message bot">Hola! Soc l'assistent de la LAN EcoTech. Com et puc ajudar avui?</div>
</div>
<div id="chat-controls">
<input type="text" id="user-input" placeholder="Escriu el teu dubte...">
<button id="btn-send" onclick="sendMessage()">Enviar</button>
<button id="btn-clear" onclick="clearChat()">Netejar</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Base de Dades
Perquè el xatbot sàpiga què respondre sobre la LAN Party, fem servir un codi en Python per obrir el fitxer on està guardada tota la informació.
def get_context():
try:
# Obrim el fitxer de text forçant la codificació utf-8 per als accents
with open('informacio.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# Ho convertim a text perquè es pugui ajuntar amb les ordres del bot
return f"Informació oficial: {json.dumps(data, ensure_ascii=False)}"
except Exception as e:
# Si el fitxer no està a la carpeta, evitem que el programa s'aturi amb un error
return "Ets l'assistent de la EcoTech LAN Party 2026."
Es posa l’opció encoding='utf-8' perquè si el text té paraules amb accents o caràcters en català (com la l·l), el programa de Python no doni un error de lectura de text
Connexió de la Xarxa entre FrontEnd i BackEnd
Tota la connexió la gestionem des de Flask en Python. Hem instal·lat el sistema CORS per evitar que el navegador web bloquegi la connexió per motius de seguretat quan fem les peticions des de la web del widget.
# Creem les instruccions del bot combinant el text del JSON i l'idioma obligatori
system_prompt = f"{get_context()}. Respon de forma breu, amable i sempre en català."
def start_new_chat():
return client.chats.create(
model="gemini-2.5-flash",
config=genai.types.GenerateContentConfig(system_instruction=system_prompt)
)
chat_session = start_new_chat()
@app.route('/chat', methods=['POST'])
def handle_chat():
global chat_session
try:
data = request.json
user_msg = data.get("message", "")
# S'envia el missatge mantenint la línia de la conversa guardada
response = chat_session.send_message(user_msg)
return jsonify({"reply": response.text.strip()})
except Exception as e:
print(f"ERROR: {str(e)}")
chat_session = start_new_chat() # Si falla la xarxa reiniciem el xat
if "429" in str(e):
return jsonify({"reply": "⚠️ El servidor de Google està saturat (Error 429). He reiniciat la connexió. Torna-ho a provar en un moment."})
return jsonify({"reply": "S'ha produït un error en la comunicació. Prova-ho de nou."}), 500
Historial de canvis i Documentació de l’equip
Tot el que hem fet en grup (l’Ali, el Jan i l’Edgar) ho hem anat guardant seguint els fitxers de control del projecte:
| CHANGELOG | CONTRIBUTING |
Hem anat apuntant les versions del projecte des de les primeres proves ([0.1.0]) fins a la darrera versió que funciona ([1.1.0]), on hem canviat el codi per fer servir la nova llibreria de Google (google-genai). | Aquí tenim posades les regles de l’equip per treballar ordenats a GitHub. Sempre fem servir els prefixos correctes als missatges quan pugem el codi (feat: per quan afegim funcions noves, fix: per quan arreglem algun error que feia petar el programa, i docs: si només modifiquem els textos d’ajuda). |
