geocat
Bot de Mastodon que fa jugar als usuaris a endevinar comarques catalanes. Tot el codi i la comunicació amb els usuaris és en català.
Publica el mapa de Catalunya amb una comarca destacada i demana quina és. Cada encert encadena una comarca nova; el primer error acaba la partida i en desa la puntuació. Hi ha un rànquing dels millors jugadors i un panell personal amb les millors partides de cadascú.
Com jugar
Menciona el compte del bot amb una d'aquestes paraules:
| Paraula | Què fa |
|---|---|
jugar |
Comença una partida nova (ratxa a 0) i mostra la primera comarca a endevinar. |
| (resposta al fil) | Escriu el nom de la comarca destacada en resposta a la pregunta oberta. |
millors |
Mostra el top 10 de jugadors per la seva millor partida individual. |
panell |
Mostra les teves 10 millors partides pròpies. |
ajuda |
Torna a mostrar aquestes instruccions. |
Com funciona una partida:
- Cada encert suma un punt a la ratxa actual i, en el mateix tut, mostra en verd totes les comarques ja encertades (amb el nom escrit només a la darrera) i en vermell la comarca nova a endevinar.
- El primer error acaba la partida: es desa la ratxa final a la base de dades, es mostra el mapa amb l'historial verd i la comarca fallada en vermell, i es publica el rànquing actualitzat.
- Si s'encerten les 43 comarques sense cap error, la partida acaba en victòria.
- Demanar
millorsopanellen resposta a una partida oberta la dona per finalitzada (es desa la ratxa que portava) abans de respondre. Demanarajuda, en canvi, no afecta cap partida activa.
Arquitectura
- Mastodon: client fet a mida amb
aiohttp(app/libraries/mastodon_api.py) — no fa servir Mastodon.py. La seva API de streaming és síncrona i no es pot fer asíncrona sense un pont fil-bloquejant; com que aquí tot ésaiohttp, el streaming (GET /api/v1/streaming/user, Server-Sent Events) és nadiamentasync. - Base de dades: PostgreSQL via
asyncpg, a través del pool propi de l'aplicació (app.services.postgres.dbpool) i les consultes aapp/libraries/asyncdb.py(Database.desa_partida,Database.obtenir_rankings,Database.obtenir_millors_partides). - Mapes:
matplotlib, sense dependència degeopandas/GDAL (app/geo/mapa_comarques.py). - Tot el bot és
asynciode cap a cap: no hi ha fils ni bridges bloquejants.
Estructura de fitxers
geocat.py # bot principal (streaming, lògica del joc)
app/
geo/
mapa_comarques.py # generació de tots els mapes
comarques.geojson # 43 comarques (juliol 2026, inclou Lluçanès i Moianès)
libraries/
mastodon_api.py # client Mastodon fet a mida (aiohttp)
asyncdb.py # consultes SQL del joc (Database)
setup.py # configuració de l'aplicació (Setup) — propi del projecte
services/
postgres.py # pool de connexió Postgres (dbpool) — propi del projecte
db/
schema.py # creació de taules (create_dbschema) — propi del projecte
imatges/ # cache de mapes generats (es crea sola en executar-se)
Requisits
pip install aiohttp asyncpg matplotlib loguru python-dotenv
Base de dades
create_dbschema() crea, entre altres, la taula partides — una fila per cada partida jugada (no un total acumulat per usuari):
CREATETABLEIFNOTEXISTSpartides(idSERIALPRIMARYKEY,acctTEXTNOTNULL,encertsINTEGERNOTNULLDEFAULT0,creat_elTIMESTAMPTZNOTNULLDEFAULTnow());CREATEINDEXidx_partides_acctONpartides(acct);El rànquing (millors) agrupa per acct i mostra la puntuació màxima de cadascú; el panell (panell) mostra les partides d'un usuari concret ordenades de major a menor puntuació.
Desat en temps real: en començar una partida es crea de seguida la fila corresponent a partides amb encerts=0, i es va actualitzant (no re-inserint) a cada encert. Això vol dir que, si el bot es reinicia enmig d'una partida, la ratxa que portava fins al darrer encert ja està desada — només es perd el que hi havia des de l'últim encert fins al moment de tallar-se (si n'hi havia cap en curs). Com a efecte secundari, mentre algú està jugant, la seva partida en curs ja apareix a millors/panell amb el recompte parcial, no només un cop acabada.
Hi ha també una taula
encerts(una fila fixa per usuari amb un total acumulat) que ve d'una primera versió del joc. Ja no la fa servir cap part del codi actual; es manté només per no trencar res si en depèn una altra part del vostre sistema.
Configuració
Al executar geocat.py per primer cop, es demanen els parametres de configuració i es crea la base de dades
Rendiment i cache
- Els mapes de pregunta i d'error es guarden a
app/imatges/la primera vegada que surt cada comarca, i es reutilitzen les següents. - Els mapes que depenen de l'estat d'una partida concreta (el de progrés durant la partida, el de resultat final, i els de
millors/panell) no es guarden a cache, perquè el seu contingut canvia cada vegada. - El
comarques.geojsond'aquest projecte ha estat netejat i simplificat (geometria original triplicada per error de conversió, ja corregit) per generar cada mapa nou en menys d'un segon. - Si mai canvieu com es dibuixen els mapes (colors, peu de pàgina, etc.), cal buidar
app/imatges/*.pngperquè els canvis es notin — la cache no es regenera sola.
Límits coneguts
- L'estat de les partides obertes (
partides_actives,ratxes,demanades,encertades) viu només en memòria del procés; si el bot es reinicia enmig d'una partida, aquella partida concreta es perd (el rànquing i les partides ja completades, no: això viu a la base de dades). - Si l'API de Mastodon respon amb un límit de peticions (429), el bot avisa l'usuari en català amb els segons d'espera reals, calculats a partir de la capçalera
X-RateLimit-Resetque Mastodon envia de veritat (Mastodon no enviaRetry-After; per defecte,POST /api/v1/mediapermet només 30 pujades cada 30 minuts, molt més estricte que el límit general de 300 peticions/5 min). Cap canvi d'estat (ratxa, comarques encertades, pregunta oberta) es dona per fet fins que la publicació a Mastodon no ha tingut èxit real, així que si es peta a mig torn, la pregunta original es manté oberta i n'hi ha prou de tornar-la a respondre un cop passi l'estona — no cal tornar a dir "jugar" ni es perd la ratxa. - Cada resposta a un encert o error inclou, al final del toot, l'últim estat conegut dels dos límits (
Límit API: X/300 ...iLímit pujada d'imatges: X/30 ...), llegit de les capçaleresX-RateLimit-*de la crida més recent — no es fa cap petició extra només per consultar-ho, per això el número de "Límit API" pot anar un torn per darrere del que s'acaba de gastar en aquell mateix missatge. - Es dona per fet un jugador amb una partida oberta alhora; dues partides simultànies del mateix usuari no estan cobertes.