Què és el Xatbot?

El Xatbot és un assistent automàtic creat per a la LAN Party Castellbisbal 2026. La idea és senzilla: qualsevol participant pot escriure una pregunta i rebre una resposta a l’instant, sense haver de buscar per la web ni esperar que algú de l’organització estigui disponible.

Durant una LAN Party es repeteixen sempre les mateixes preguntes: “A quina hora comença el torneig?”, “On es pot menjar?”, “Puc portar la meva cadira?”. Respondre-les una per una manualment és lent i cansat. Amb el xatbot, qualsevol persona rep la resposta en pocs segons, a qualsevol hora del dia.

Com funciona?

Quan l’usuari escriu una pregunta a la finestra de xat, el missatge s’envia al nostre servidor. El servidor consulta la intel·ligència artificial de Google (un model anomenat Gemini) i genera una resposta basada en la informació real de la LAN Party que hem introduït nosaltres prèviament.

El sistema té tres parts principals: el frontend (la finestra de xat que veu l’usuari), el backend (el servidor que processa les preguntes) i les dades (fitxers JSON amb tota la informació de l’esdeveniment).


Backend: el servidor Flask

El backend està fet amb Python i utilitza les eines següents:

  • Flask: un servidor web lleuger que rep les preguntes i retorna les respostes.
  • Flask-CORS: permet que el navegador de l’usuari es pugui comunicar amb el servidor (sense això, el navegador bloqueja la connexió per seguretat).
  • Google Gemini: la intel·ligència artificial que genera les respostes.
  • ngrok: crea una adreça web pública temporal per al servidor.
  • Google Colab: entorn gratuït al núvol des d’on s’executa el servidor.
# 1. INSTAL·LACIÓ
!pip install -q -U google-genai flask-cors pyngrok beautifulsoup4 requests
!pip install requests==2.32.4
# ==========================================
# 1. NETEJA I DEPENDÈNCIES
# ==========================================
!pkill -f ngrok
import time, json, os
from flask import Flask, request, jsonify
from flask_cors import CORS
from google import genai
from google.colab import userdata
from pyngrok import ngrok
import IPython

# ==========================================
# 2. CÀRREGA DE CONTEXT OPTIMITZADA
# ==========================================
def carregar_dades():
docs = [‘Lanparty.json’, ‘Faq.json’, ‘Normativa.json’, ‘dades_party.json’]
resum = “”
for d in docs:
if os.path.exists(d):
with open(d, ‘r’, encoding=’utf-8′) as f:
resum += f.read()[:500] # Tall de seguretat per evitar lag
return resum

# ==========================================
# 3. BACKEND (XATBOT PURPLE EDITION)
# ==========================================
app = Flask(__name__)
CORS(app)

API_KEY = userdata.get(“GOOGLE_API_KEY”)
client = genai.Client(api_key=API_KEY)

def init_xatbot():
return client.chats.create(
model=”gemini-2.5-flash”,
config=genai.types.GenerateContentConfig(
system_instruction=f”Ets Xatbot de la LAN Party Castellbisbal. Context: {carregar_dades()}. Estil gamer, català, breu i divertit.”
)
)

chat_session = init_xatbot()

@app.route(‘/chat’, methods=[‘POST’])
def handle_chat():
global chat_session
# [ÚS DE LA IA] Implementació d’un delay artificial de 0 segons demanat per l’usuari.
time.sleep(0)
try:
user_msg = request.json.get(“message”, “”)
response = chat_session.send_message(user_msg)
return jsonify({“reply”: response.text.strip()})
except:
chat_session = init_xatbot()
return jsonify({“reply”: “S’ha perdut la connexió al servidor lila… Torna a provar-ho!”})

# ==========================================
# 4. LLANÇAMENT NGROK
# ==========================================
NGROK_TOKEN = userdata.get(“NGROK_TOKEN”)
ngrok.set_auth_token(NGROK_TOKEN)
public_url = ngrok.connect(5000).public_url

print(f”💜 XATBOT ONLINE: {public_url}”)

# ==========================================
# 5. FRONTEND PURPLE GAMER RGB
# ==========================================
frontend_purple = f”””
<div id=”chat-ui” style=”max-width:400px; margin:auto; background:#0d0216; border: 2px solid #a020f0; border-radius:15px; overflow:hidden; font-family:’Segoe UI’, sans-serif; box-shadow: 0 0 15px #a020f0;”>
<div style=”background: #1a052d; color:#e0b0ff; padding:15px; text-align:center; font-weight:bold; border-bottom: 2px solid #a020f0; letter-spacing: 2px;”>
🔮 XATBOT CASTELLBISBAL 🔮
</div>
<div id=”display” style=”height:380px; overflow-y:auto; padding:15px; display:flex; flex-direction:column; gap:12px; background: #0d0216;”>
<div style=”background:#2d0a4e; color:#e0b0ff; padding:10px; border-radius:15px 15px 15px 0px; align-self:flex-start; max-width:85%; font-size:14px; border: 1px solid #a020f0;”>
Hola gamer! Soc en Xatbot. Quina és la teva consulta? 👾
</div>
</div>
<div id=”typing” style=”color:#a020f0; font-size:10px; padding-left:15px; height:15px; visibility:hidden;”>Xatbot està escrivint…</div>
<div style=”padding:10px; background:#1a052d; display:flex; gap:8px;”>
<input id=”user_in” type=”text” style=”flex:1; background:#0d0216; color:white; border:1px solid #a020f0; padding:10px; border-radius:10px; outline:none;” placeholder=”Escriu…”>
<button onclick=”askBot()” style=”background:#a020f0; color:white; border:none; padding:10px 15px; font-weight:bold; cursor:pointer; border-radius:10px; box-shadow: 0 0 5px #a020f0;”>GG</button>
</div>
</div>

<script>
async function askBot() {{
const input = document.getElementById(‘user_in’);
const display = document.getElementById(‘display’);
const typing = document.getElementById(‘typing’);
const msg = input.value.trim();
if(!msg) return;

// Missatge usuari (Sense prefix)
display.innerHTML += `<div style=”background:#a020f0; color:white; padding:10px; border-radius:15px 15px 0px 15px; align-self:flex-end; max-width:85%; font-size:14px;”>${{msg}}</div>`;
input.value = ”;
display.scrollTop = display.scrollHeight;

// Activar indicador d’escriptura
typing.style.visibility = ‘visible’;

try {{
const res = await fetch(‘{public_url}/chat’, {{
method: ‘POST’,
headers: {{‘Content-Type’: ‘application/json’}},
body: JSON.stringify({{message: msg}})
}});
const data = await res.json();

typing.style.visibility = ‘hidden’;
// Resposta bot (Sense prefix)
display.innerHTML += `<div style=”background:#2d0a4e; color:#e0b0ff; padding:10px; border-radius:15px 15px 15px 0px; align-self:flex-start; max-width:85%; font-size:14px; border: 1px solid #a020f0;”>${{data.reply}}</div>`;
}} catch(e) {{
typing.style.visibility = ‘hidden’;
display.innerHTML += `<div style=”color:red; text-align:center; font-size:12px;”>Connexió fallida</div>`;
}}
display.scrollTop = display.scrollHeight;
}}
document.getElementById(‘user_in’).addEventListener(‘keypress’, (e) => {{ if(e.key === ‘Enter’) askBot(); }});
</script>
“””

IPython.display.display(IPython.display.HTML(frontend_purple))
app.run(port=5000)

Problemes que vam trobar i com els vam solucionar:

Durant el desenvolupament ens vam topar amb tres problemes concrets. El primer va ser l’error HTTP 429 (quota superada): el model gemini-pro tenia restriccions molt severes a la capa gratuïta, i la solució va ser canviar a gemini-2.5-flash, que és més ràpid i té una quota molt més generosa. El segon va ser el bloqueig CORS: el navegador bloquejava les peticions perquè el frontend i el backend estaven en dominis diferents, i vam solucionar-ho afegint la llibreria flask-cors. El tercer va ser la pantalla d’advertència de ngrok: quan el frontend feia una petició, ngrok mostrava primer una pàgina HTML d’advertència i el nostre JavaScript es trencava perquè esperava rebre JSON. Ho vam resoldre assegurant que les capçaleres de la petició fossin correctes.

Frontend: la interfície de l’usuari

La interfície del xatbot està feta amb HTML5, CSS3 i JavaScript estàndard. No necessita cap llibreria externa. L’estètica és de tipus gamer, amb fons fosc i colors morats.

Funcions implementades: els missatges de l’usuari surten a la dreta en morat intens, les respostes del bot surten a l’esquerra en lila clar, apareix l’indicador “Xatbot està escrivint…” mentre s’espera la resposta, la finestra es desplaça automàticament per veure sempre el darrer missatge, i es pot enviar un missatge prement la tecla Enter.

<div id=”chat-ui” style=”max-width:400px; margin:10px auto; background:#0d0216; border: 2px solid #a020f0; border-radius:15px; box-shadow: 0 0 20px #a020f0;”> <div id=”header” style=”background:#1a052d; color:#e0b0ff; padding:15px; text-align:center; font-weight:bold; border-bottom:2px solid #a020f0;”> 🔮 XATBOT CASTELLBISBAL 🔮 </div> <div id=”display” style=”height:380px; overflow-y:auto; padding:15px; display:flex; flex-direction:column; gap:12px; background:#0d0216;”> <div class=”bot-msg”>Benvingut a la LAN Party! Com puc ajudar-te? 👾</div> </div> <div id=”typing” style=”color:#a020f0; font-size:11px; padding:0 15px 5px; height:15px; font-style:italic; visibility:hidden;”> Xatbot està escrivint… </div> <div id=”input-area” style=”padding:10px; background:#1a052d; display:flex; gap:8px;”> <input id=”user_in” type=”text” placeholder=”Escriu missatge…” style=”flex:1; background:#0d0216; color:white; border:1px solid #a020f0; padding:10px; border-radius:10px; outline:none;”> <button onclick=”askBot()” style=”background:#a020f0; color:white; border:none; padding:10px 15px; font-weight:bold; cursor:pointer; border-radius:10px;”>GG</button> </div> </div> <script> const NGROK_URL = “https://el-teu-servidor.ngrok-free.dev”; async function askBot() { const input = document.getElementById(‘user_in’); const display = document.getElementById(‘display’); const typing = document.getElementById(‘typing’); const msg = input.value.trim(); if (!msg) return; display.innerHTML += `<div style=”background:#a020f0; color:white; padding:10px; border-radius:15px 15px 0 15px; align-self:flex-end; max-width:85%; font-size:14px;”>${msg}</div>`; input.value = ”; display.scrollTop = display.scrollHeight; typing.style.visibility = ‘visible’; try { const res = await fetch(`${NGROK_URL}/chat`, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ message: msg }) }); const data = await res.json(); setTimeout(() => { typing.style.visibility = ‘hidden’; display.innerHTML += `<div style=”background:#2d0a4e; color:#e0b0ff; padding:10px; border-radius:15px 15px 15px 0; align-self:flex-start; max-width:85%; font-size:14px; border:1px solid #a020f0;”>${data.reply}</div>`; display.scrollTop = display.scrollHeight; }, 500); } catch(e) { typing.style.visibility = ‘hidden’; display.innerHTML += `<div style=”color:red; text-align:center; font-size:12px;”> Error de connexió</div>`; } } document.getElementById(‘user_in’).addEventListener(‘keypress’, (e) => { if (e.key === ‘Enter’) askBot(); }); </script>

Gestió de dades: els fitxers JSON

Tota la informació de la LAN Party (horaris, normativa, preguntes freqüents, tornejos) es guarda en fitxers JSON. Quan el xatbot rep una pregunta, la IA consulta aquests fitxers i construeix una resposta basada en dades reals. Això és important perquè evita que la IA inventi informació sobre l’esdeveniment.

Hem usat quatre fitxers: Lanparty.json (informació general), Faq.json (preguntes freqüents), Normativa.json (regles de la LAN Party) i dades_party.json (configuració de xarxa i tornejos).

Per seguretat, el codi gestiona dos tipus d’errors: si un fitxer no existeix (FileNotFoundError), el sistema avisa per consola en lloc de trencar-se; si el fitxer té un error de sintaxi (JSONDecodeError), el bot el detecta i protegeix l’execució.

Gestió de dades: els fitxers JSON

Tota la informació de la LAN Party (horaris, normativa, preguntes freqüents, tornejos) es guarda en fitxers JSON. Quan el xatbot rep una pregunta, la IA consulta aquests fitxers i construeix una resposta basada en dades reals. Això és important perquè evita que la IA inventi informació sobre l’esdeveniment.

Hem usat quatre fitxers: Lanparty.json (informació general), Faq.json (preguntes freqüents), Normativa.json (regles de la LAN Party) i dades_party.json (configuració de xarxa i tornejos).

Per seguretat, el codi gestiona dos tipus d’errors: si un fitxer no existeix (FileNotFoundError), el sistema avisa per consola en lloc de trencar-se; si el fitxer té un error de sintaxi (JSONDecodeError), el bot el detecta i protegeix l’execució.

GitHub: CHANGELOG i CONTRIBUTING

CHANGELOG

El CHANGELOG és un document que registra tots els canvis fets al projecte. Permet saber en quin punt estava el codi en cada moment i quines funcionalitats s’han afegit o corregit. A GitHub es troba com el fitxer CHANGELOG.md a l’arrel del repositori.

El fitxer CONTRIBUTING.md explica com qualsevol persona pot col·laborar al projecte: afegir funcionalitats, corregir errors o millorar la documentació.