# VANTS
> Documentación interna de VANTS, la plataforma de esports: repositorios y cómo se conectan, arquitectura, bus de eventos, API pública, esquema de la base de datos, bot de Discord y seguridad.
> ## Agent Instructions
> Documentación interna de VANTS, la plataforma de esports, escrita en español. Cubre siete repositorios de la organización de GitHub VANTSNET y dos de la cuenta feispla: el prototipo VANTSNET, que se llama igual que la organización, y vantsoport.
> Empieza por /mapa-del-codigo para saber qué repositorio y qué carpeta tocar, y consulta /glosario para los términos propios como pt_, mm_, RR, VANTS-BUS o VANTCALL.
> Cuando un componente existe en varios repositorios, estas páginas describen la versión de Vantsgames salvo que digan otra cosa. El esquema de la base de datos se versiona en VantsBasededatos.
> La web y la API pública están en https://vants.vants.workers.dev. Rutas y respuestas: /referencia/api-publica. Tablas y estados: /referencia/esquema-base-de-datos.
> No inventes rutas, tablas, comandos ni variables de entorno: si algo no aparece aquí, revisa el código del repositorio correspondiente.
# Documentación para agentes de IA
Source: https://vantsgg.mintlify.app/agentes
Cómo conectar Claude, Cursor, VS Code o Codex a esta documentación por MCP, llms.txt o Markdown, y qué reglas siguen los agentes en el código de VANTS.
Esta documentación está preparada para que un agente de IA la consulte igual que una persona. Puedes conectarla a tu editor por MCP, darle a un modelo el índice `llms.txt` o leer cualquier página en Markdown. Esta página explica cada opción y resume las reglas que siguen los agentes cuando trabajan en el código de VANTS.
## Por dónde empezar
**1. Mapa del código**
[Mapa del código](/mapa-del-codigo) indica qué repositorio y qué archivo tocar para cada cambio.
**2. Glosario**
[Glosario](/glosario) explica prefijos como `pt_` y `mm_` y nombres como RR, VANTS-BUS o VANTCALL.
**3. Arquitectura**
[Arquitectura general](/arquitectura) muestra cómo se conectan el Worker, Neon, Auth0, Stripe, el bot y los workflows.
**4. Referencia**
[API pública](/referencia/api-publica), [Esquema de la base de datos](/referencia/esquema-base-de-datos), [Bot de Discord](/referencia/bot-de-discord) y [Seguridad](/referencia/seguridad) tienen el detalle de cada área.
## Formas de leer esta documentación
| Recurso | Dirección | Para qué sirve |
| - | - | - |
| Servidor MCP | `https://vantsgg.mintlify.app/mcp` | Buscar y leer páginas desde Claude, Cursor, VS Code, Codex u otro cliente MCP |
| Índice | [llms.txt](https://vantsgg.mintlify.app/llms.txt) | Lista de todas las páginas con su descripción, en el orden del menú |
| Documentación completa | [llms-full.txt](https://vantsgg.mintlify.app/llms-full.txt) | Todo el contenido en un solo archivo de texto |
| Página en Markdown | Añade `.md` a la dirección de la página, por ejemplo [glosario.md](https://vantsgg.mintlify.app/glosario.md) | Una página concreta, sin HTML |
El menú de la parte superior de cada página permite copiarla o verla en Markdown, abrirla en ChatGPT, Claude o Perplexity, copiar la dirección del servidor MCP e instalarlo en Cursor o VS Code.
## Conectar el servidor MCP
El servidor no pide autenticación. Permite buscar en esta documentación y leer sus páginas como si fueran archivos.
**Claude Code**
```bash theme={null}
claude mcp add --transport http vants-docs https://vantsgg.mintlify.app/mcp
```
**Cursor**
Añade el servidor a `.cursor/mcp.json` en el proyecto, o a `~/.cursor/mcp.json` para todos tus proyectos:
```json theme={null}
{
"mcpServers": {
"vants-docs": {
"url": "https://vantsgg.mintlify.app/mcp"
}
}
}
```
**VS Code**
Añade el servidor a `.vscode/mcp.json`:
```json theme={null}
{
"servers": {
"vants-docs": {
"type": "http",
"url": "https://vantsgg.mintlify.app/mcp"
}
}
}
```
**Codex**
Añade el servidor a `~/.codex/config.toml`:
```toml theme={null}
[mcp_servers.vants-docs]
url = "https://vantsgg.mintlify.app/mcp"
```
## Cómo usar esta documentación
* Si una página no coincide con el código, manda el código. Para el esquema de la base de datos, mandan las migraciones de [VantsBasededatos](/repos/vants-base-de-datos).
* Las páginas de Referencia describen la versión de Vantsgames, salvo que indiquen otro repositorio.
* No des por hecho rutas, tablas, comandos ni variables que no aparezcan aquí: búscalos en el repositorio antes de usarlos.
* Para editar esta documentación, sigue las reglas de `AGENTS.md` en la raíz del repositorio de la documentación.
## Reglas para agentes en el código
Vantsgames y vants-dev-launcher tienen en la raíz un `CLAUDE.md` con el acuerdo de trabajo del equipo con los agentes. Léelo antes de cambiar código. Sus reglas principales:
* **Contexto antes que prompt**: cada encargo indica qué archivos tocar y cuáles no, qué está hecho y cuál es el criterio de aceptación.
* **Nada de migraciones sin revisión**: el esquema se diseña antes de escribir la migración y el propietario la revisa. Ningún agente ejecuta SQL destructivo (`DROP`, `TRUNCATE` o `DELETE` masivo) contra Neon.
* **Verificado, o no está terminado**: ejecuta `node --check` en cada archivo JavaScript que toques y prueba en el navegador el flujo afectado. Si algo no se pudo verificar, dilo.
* **Imita el repositorio**: busca antes de crear, usa IIFE con `'use strict'` y `escHtml` para todo texto dinámico, y no añadas frameworks nuevos a la web estática. Un mismo commit no mezcla refactor y funcionalidad.
* **A producción no se llega sin una persona**: ningún agente hace push a `main`. Trabaja en una rama y abre un PR; el propietario lo fusiona, y fusionar despliega.
* **Lo prohibido va en hooks**: `.claude/settings.json` bloquea las acciones peligrosas antes de ejecutarlas, como tocar archivos `.env` o de claves, `git push --force` o editar `.github/workflows/` sin que lo pida el propietario.
* **Un agente, un rol**: quien escribe un cambio no lo aprueba. Otra pasada, de un agente o de una persona, revisa el diff antes del PR.
* **Autenticado no es autorizado**: toda consulta que devuelva datos de alguien comprueba que le pertenecen (RLS de Neon y `auth0_sub`). Los errores indican el código y el campo que falla, nunca trazas ni SQL.
* **Todo se ejecuta dos veces**: los webhooks y las integraciones son idempotentes, y ninguna consulta a Neon va dentro de un bucle del frontend.
* **Decisiones con fecha de caducidad**: toda decisión difícil de deshacer se registra en `DECISIONS.md` con su contexto, motivo, consecuencias, fecha y qué la invalidaría.
### Además, en vants-dev-launcher
* La web estática está en tres carpetas idénticas: la raíz, `public/` y `vants-server/public/`. Aplica cada cambio en las tres y comprueba que `node scripts/build-webapp.mjs --check` termina sin diferencias.
* Si cambias un JavaScript o un CSS que cargan las páginas, sube su versión (`archivo.js?v=N` pasa a `N+1`) en todos los HTML que lo cargan, en las tres carpetas, y en `webapp/src/layouts/Base.astro`.
# Arquitectura general
Source: https://vantsgg.mintlify.app/arquitectura
Piezas de VANTS y cómo se conectan: Worker de Cloudflare, Neon, Auth0, Neon Functions, bot y agente, recorrido de una petición, despliegues y decisiones clave.
VANTS funciona sobre un Worker de Cloudflare que sirve la web y la API, una base de datos Postgres en Neon y Auth0 para la identidad. Alrededor hay Neon Functions, un bot de Discord, un agente que vigila la plataforma y un bus de eventos que conecta los proyectos. Esta página describe cada pieza, el recorrido de una petición, los despliegues y las decisiones que condicionan el código.
## Componentes
| Capa | Tecnología | Dónde está |
| - | - | - |
| Frontend | HTML, CSS y JavaScript sin framework | `vants-server/public/` |
| Backend | Worker de Cloudflare `vants`: seguridad, rutas, API pública JSON, eventos de Discord e inscripciones | `vants-server/worker.js` |
| Datos | Neon Postgres, a través de la Data API y de Neon Functions | `neon-functions/`. El esquema se versiona en [VantsBasededatos](/repos/vants-base-de-datos) |
| Identidad | Auth0 | `vants-server/worker.js` valida el JWT de la sesión contra Auth0 |
| Pagos | Stripe, con sus webhooks en Neon Functions | `neon-functions/stripe-checkout/` y `neon-functions/stripe-webhook/` |
| Bot | VANTCALL, bot de Discord en Python (`discord.py`) | `bot/`. Ver [Bot de Discord](/referencia/bot-de-discord) |
| Agente | Agente VANTS: vigila la plataforma, registra incidencias y ejecuta órdenes | `services/agent-service/` |
| Eventos | VANTS-BUS, bus de eventos entre proyectos | `vants-bus/`. Ver [Bus de eventos](/referencia/esquema-base-de-datos#bus-de-eventos) |
| Clientes | Web, launcher de escritorio Electron y app Android (TWA) | `android/`, y el launcher en [vants-dev-launcher](/repos/vants-dev-launcher) |
Las rutas son las de [Vantsgames](/repos/vantsgames). vants-dev-launcher añade una home en Astro con islas React y Vue (`webapp/`) y servicios en contenedores Docker (`services/api-gateway/` y `services/matchmaking-service/`), pero el Worker sigue siendo el único servidor de producción. Para encontrar el archivo exacto de cada cambio, consulta [Mapa del código](/mapa-del-codigo).
## Recorrido de una petición
```mermaid theme={null}
flowchart LR
C[Navegador o app] --> W[Worker vants]
W --> S["Archivos estáticos (vants-server/public)"]
W -->|lecturas públicas| V[Neon Function vantsdata]
W -->|escrituras con el JWT de Auth0| D[Neon Data API]
W -->|valida la sesión| A[Auth0]
V --> P[(Neon Postgres)]
D -->|RLS de cada tabla| P
```
1. El Worker recibe todas las peticiones y aplica la protección contra bots, los límites de uso y las cabeceras de seguridad.
2. Las páginas y los archivos estáticos salen de `vants-server/public/`.
3. Las lecturas públicas pasan por la Neon Function `vantsdata`, que solo acepta `GET` sobre una lista cerrada de tablas públicas.
4. Las escrituras van siempre a la Neon Data API. El Worker reenvía el JWT de Auth0 del usuario y la RLS de cada tabla decide si la operación se permite.
El Worker no guarda credenciales de la base de datos. Las rutas, los parámetros y las respuestas de la API están en [API pública](/referencia/api-publica), y las tablas en [Esquema de la base de datos](/referencia/esquema-base-de-datos).
## Despliegues
Un push a `main` lanza workflows de GitHub Actions independientes, según los archivos que cambien:
| Cambia | Workflow | Qué hace |
| - | - | - |
| `scripts/`, `vants-server/`, `neon-functions/` o `db/` | `tests` | Pruebas automáticas. |
| `db/migrations/` | `neon-migrate` | Migraciones de la base de datos (idempotentes). |
| `neon-functions/` | `neon-deploy-functions` | Despliegue de las funciones de Neon. |
| `vants-server/` | `cloudflare-deploy` | Despliegue del Worker con `wrangler deploy`. |
Son workflows separados: el despliegue no espera a que terminen las pruebas. Los despliegues y las migraciones comparten una cola y se ejecutan de uno en uno. Otros workflows se ejecutan con un horario, como la sincronización con FACEIT, el monitor o el agente; la lista completa está en [Workflows](/repos/vantsgames#workflows).
Más detalle en [Vantsgames](/repos/vantsgames), [VantsBasededatos](/repos/vants-base-de-datos) y [Seguridad](/referencia/seguridad#despliegues-y-ci).
## Decisiones
Las decisiones que costaría deshacer se registran en `DECISIONS.md`, en la raíz de Vantsgames y de vants-dev-launcher. Estas son las que más condicionan el código:
| Decisión | Qué implica |
| - | - |
| D1 | No se reescribe a Next.js: se mantienen el Worker, el frontend estático y Neon. |
| D4 | Los datos de demostración llevan `is_demo = true` y la web los marca como DEMO. |
| D5 | La web está preparada para español e inglés. Las páginas de torneos usan rutas con `/es/` y `/en/`. |
| D6 | Sin redes de analítica ni de publicidad de terceros. |
| D9 | Los datos reales de FACEIT llegan por su Data API v4 oficial y una sincronización programada, sin scraping. |
| D10 | `pt_players` es un catálogo público separado de la tabla `players`. |
| D11 | La home en Astro y los servicios en contenedores complementan al Worker, que sigue siendo el único servidor de producción. |
| D12 | Identidad visual MYKD: rojo y negro. |
| D13 | La numeración de las migraciones está congelada: no se renombran migraciones antiguas. |
Los términos propios de esta página están definidos en [Glosario](/glosario).
# Cómo se conectan los repositorios
Source: https://vantsgg.mintlify.app/conexiones-entre-repositorios
Qué conecta cada repositorio de VANTS y cómo circulan los eventos por VANTS-BUS: quién publica y consume cada tipo, firma de las peticiones y clientes.
Varios repositorios de VANTS usan la misma base de datos Neon y se avisan de lo que pasa a través de **VANTS-BUS**, un bus de eventos. Esta página muestra qué conecta con qué, qué eventos circulan por el bus y quién los publica y los consume. Las piezas internas de cada repositorio están en [Arquitectura general](/arquitectura) y en la página de cada repositorio.
## Mapa de conexiones
Los recuadros agrupan las piezas que viven en un mismo repositorio.
```mermaid actions={false} theme={null}
flowchart LR
F[FACEIT] -->|webhooks| FW[vants-faceit-webhook]
FW -->|guarda cada webhook| N[(Neon Postgres)]
FW -->|publica eventos de partidos| B[VANTS-BUS]
B -->|guarda los eventos| N
B -->|eventos| FA[flask-agents]
FA -->|publica post.generated| B
subgraph PW [Vantsgames]
W[Worker vants]
CW[vants-consumer-playvants]
end
subgraph LAU [vants-dev-launcher]
H[VANTS HUB]
end
B -->|eventos| H
B -->|eventos| CW
CW -->|copia a vants_feed| N
W -->|publica inscripciones, clips y resultados| B
W -->|lee y escribe| N
W -->|valida la sesión| A[Auth0]
W -->|publica clips| C[vants-clips]
DB[VantsBasededatos] -->|migraciones| N
S[Vants-Proteger] -->|login| A
S -->|datos| N
subgraph VN [VANTSNET]
T[API de torneos]
end
T -->|publica torneos, inscripciones y resultados| B
subgraph IND [Independientes]
VS[vantsoport]
end
```
## Qué conecta cada repositorio
| Repositorio | Se conecta con | Cómo |
| - | - | - |
| [Vantsgames](/repos/vantsgames) | Neon, Auth0, vants-clips y VANTS-BUS | El Worker `vants` lee y escribe en Neon, valida la sesión con Auth0 y publica los clips destacados en vants-clips. En `vants-bus/` están el cliente del bus y el Worker `vants-consumer-playvants`, que copia los eventos a `vants_feed`. El Worker `vants` también publica al bus: `inscription.created` al inscribir un equipo, `clip.published` al destacar un clip y `match.finished` al cerrar una partida de bracket |
| [vants-dev-launcher](/repos/vants-dev-launcher) | Vantsgames y VANTS-BUS | Comparte con Vantsgames el Worker, las Neon Functions, el bot y el agente. Su launcher de escritorio, VANTS HUB, escucha el bus y muestra notificaciones |
| [Vants-Proteger](/repos/vants-proteger) | Auth0 y Neon | Sitio estático con autenticación Auth0 y datos en Neon |
| [VantsBasededatos](/repos/vants-base-de-datos) | Neon | Sus migraciones definen el esquema de Neon que usan los demás repositorios |
| [vants-clips](/repos/vants-clips) | Vantsgames | El Worker `vants` crea un release por cada clip y actualiza `manifest.json` |
| [vants-faceit-webhook](/repos/vants-faceit-webhook) | FACEIT, Neon y VANTS-BUS | Guarda en Neon cada webhook de FACEIT y publica en el bus los eventos de partidos |
| [flask-agents](/repos/flask-agents) | VANTS-BUS | Escucha el bus y publica `post.generated` con los textos que redactan los agentes |
| [VANTSNET](/repos/vantsnet) | VANTS-BUS | Publica los eventos de su prototipo (torneos, inscripciones y resultados) con el cliente TypeScript de `src/lib/bus/`. No comparte base de datos ni código con los demás repositorios |
| [vantsoport](/repos/vantsoport) | Ninguno | Soporte de la extensión Vants para GitHub Copilot Chat, que trabaja con GitHub y n8n. No usa el bus |
## VANTS-BUS
VANTS-BUS es un Worker de Cloudflare (`vants-bus`) disponible en `https://vants-bus.vants.workers.dev`. Recibe los eventos que publican los proyectos, los guarda en Neon y los entrega a quien los pida. Los eventos no se modifican y se conservan 30 días; sus tablas están en [Bus de eventos](/referencia/esquema-base-de-datos#bus-de-eventos). Estos repositorios contienen los clientes del bus, no el código del Worker `vants-bus`.
Cada evento tiene un `id` (`evt_` más un ULID, ordenable), un `type`, el `source` (el proyecto que lo publicó), la fecha `ts` y los datos en `data`.
### Dónde corre cada pieza
El diagrama agrupa las piezas según dónde se ejecutan y muestra en qué tablas escribe cada una. Todas las tablas están en una sola base de datos de Neon; [VantsBasededatos](/repos/vants-base-de-datos) crea una rama por cada PR para probar las migraciones.
```mermaid theme={null}
flowchart TB
subgraph EXT ["Servicios externos"]
AU[Auth0]
FC[FACEIT]
end
subgraph CF ["Workers de Cloudflare"]
WV[Worker vants]
FW[vants-faceit-webhook]
BUS[VANTS-BUS]
CP[vants-consumer-playvants]
end
subgraph APPS ["Aplicaciones"]
WEB[Web de Vantsgames]
HUB["VANTS HUB (Electron)"]
FA["flask-agents (Python)"]
end
subgraph NEON ["Neon PostgreSQL: una base de datos, con una rama por PR"]
DOM[("Tablas del dominio: jugadores, torneos, matchmaking...")]
EV[(vants_events)]
FEED[(vants_feed)]
DEDUP[(vants_bus_dedup)]
FWE[(faceit_webhook_events)]
end
WEB -->|API| WV
WV -->|valida la sesión| AU
WV -->|lee y escribe| DOM
WV -->|"publica inscription.created, clip.published y match.finished de brackets"| BUS
FC -->|"POST /api/webhooks/faceit con su secreto"| FW
FW -->|guarda cada webhook| FWE
FW -->|"evita duplicados por event_id"| DEDUP
FW -->|"publica match.finished, match.scheduled y match.canceled"| BUS
BUS -->|guarda los eventos| EV
BUS -->|"long-polling de 20 s: 7 tipos"| HUB
BUS -->|"long-polling de 20 s: 9 tipos"| FA
FA -->|publica post.generated| BUS
BUS -->|"cada 15 min: todos los tipos"| CP
CP -->|copia los eventos| FEED
```
En `main`, la web todavía no lee `vants_feed`.
### Proyectos conectados
Cada proyecto se identifica con su nombre en la cabecera `X-VANTS-Project` y firma con su propio secreto.
| Proyecto | Repositorio | Qué hace en el bus |
| - | - | - |
| `faceit-webhook` | vants-faceit-webhook | Publica los eventos de partidos que llegan de FACEIT |
| `flask-agents` | flask-agents | Escucha nueve tipos de evento y publica `post.generated` |
| `vants-launcher` | vants-dev-launcher | VANTS HUB escucha siete tipos de evento y muestra notificaciones |
| `playvants-web` | Vantsgames | El Worker publica inscripciones, clips y resultados de bracket, y `vants-consumer-playvants` copia todos los eventos a `vants_feed` cada 15 minutos |
| `vantsnet` | VANTSNET | Publica los eventos de torneos, inscripciones y partidas de su prototipo |
Todos se configuran con `VANTS_BUS_URL` y `VANTS_BUS_SECRET`. Si faltan, la conexión con el bus queda apagada y el resto del proyecto funciona igual.
### Quién publica y quién consume cada evento
El contrato tiene 12 tipos de evento, definidos en la constante `EVENTS` de cada cliente. El diagrama y la tabla indican qué código los publica y los consume hoy. Además de los consumidores de la tabla, `vants-consumer-playvants` copia todos los tipos a `vants_feed`.
En el diagrama, el color de cada tipo indica quién lo consume: azul, flask-agents y VANTS HUB; verde, solo flask-agents; amarillo, solo VANTS HUB, y gris, nadie todavía.
```mermaid theme={null}
flowchart LR
subgraph PUB ["1 · Publican"]
P1[VANTSNET]
P2[vants-faceit-webhook]
P3["Vantsgames (Worker vants)"]
P4[flask-agents]
end
subgraph BUS ["2 · VANTS-BUS: contrato de 12 tipos"]
E1[tournament.inscriptions_opened]
E2[match.scheduled]
E3[match.finished]
E4[match.canceled]
E5[alert.detected]
E6[clip.highlights_ready]
E7[game.session_started]
E8[inscription.created]
E9[clip.published]
E10[post.generated]
E11[game.session_ended]
E12[anticheat.passed]
end
subgraph CON ["3 · Consumen"]
C1["flask-agents: escucha 9 tipos"]
C2["VANTS HUB: escucha 7 tipos"]
C3["vants-consumer-playvants: copia los 12 tipos a vants_feed cada 15 min"]
end
P1 --> E1 & E2 & E3 & E8
P2 --> E2 & E3 & E4
P3 --> E3 & E8 & E9
P4 --> E10
BUS -->|"los 6 azules y los 3 verdes"| C1
BUS -->|"los 6 azules y post.generated"| C2
BUS -.->|todos| C3
classDef ambos fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef agentes fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef hub fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef nadie fill:#f3f4f6,stroke:#9ca3af,color:#4b5563
class E1,E2,E3,E4,E5,E6 ambos
class E7,E8,E9 agentes
class E10 hub
class E11,E12 nadie
```
| Evento | Lo publica | Lo consumen |
| - | - | - |
| `match.finished` | vants-faceit-webhook, Vantsgames (brackets de torneos) y VANTSNET | flask-agents y VANTS HUB |
| `match.scheduled` | vants-faceit-webhook y VANTSNET | flask-agents y VANTS HUB |
| `match.canceled` | vants-faceit-webhook | flask-agents y VANTS HUB |
| `post.generated` | flask-agents | VANTS HUB |
| `tournament.inscriptions_opened` | VANTSNET | flask-agents y VANTS HUB |
| `clip.highlights_ready` | Ninguno todavía | flask-agents y VANTS HUB |
| `alert.detected` | Ninguno todavía | flask-agents y VANTS HUB |
| `inscription.created` | Vantsgames y VANTSNET | flask-agents |
| `clip.published` | Vantsgames | flask-agents |
| `game.session_started` | Ninguno todavía | flask-agents |
| `game.session_ended` | Ninguno todavía | Ninguno todavía |
| `anticheat.passed` | Ninguno todavía | Ninguno todavía |
El Worker `vants` publica desde `vants-server/worker.js` con el cliente de `vants-bus/`, y la API de torneos de VANTSNET publica desde `src/lib/bus/`. Los dos envían sin bloquear la respuesta y quedan apagados si falta su secreto. El ejemplo `snippet-worker.js` sigue en `vants-bus/` como referencia.
### Datos de los eventos publicados
| Evento | Campos de `data` |
| - | - |
| `match.finished` | `matchId`, `game`, `a` y `b` (equipos), `sa` y `sb` (marcador de cada equipo) y `finishedAt` |
| `match.scheduled` | `matchId`, `game` y `startsAt` |
| `match.canceled` | `matchId`, `game` y `canceledAt` |
| `post.generated` | `channel` (`discord`), `kind` (`match_result` o `tournament_open`), `text` y, en los resultados, `matchId` |
| `tournament.inscriptions_opened` | `tournamentId` (slug del torneo), `nombre` y `game` |
| `inscription.created` | `tournamentId` (slug del torneo), `teamId` (equipo inscrito) y `game` |
| `clip.published` | `clipId`, `titulo`, `juego`, `nick`, `video` y `thumb` |
En los eventos de partidos, `game` va en minúsculas y `csgo` se convierte en `cs2`. La correspondencia con los webhooks de FACEIT está en [vants-faceit-webhook](/repos/vants-faceit-webhook).
### Recorrido de un resultado
```mermaid theme={null}
sequenceDiagram
autonumber
actor F as FACEIT
participant W as vants-faceit-webhook
(Worker de Cloudflare)
participant N as Neon
(PostgreSQL)
participant B as VANTS-BUS
(vants-bus.vants.workers.dev)
participant A as flask-agents
(Python)
participant H as VANTS HUB
(Electron, escritorio)
participant C as vants-consumer-playvants
(Worker, cron cada 15 min)
F->>W: POST /api/webhooks/faceit
match_status_finished + secreto
W->>N: INSERT INTO faceit_webhook_events
(event_type, payload)
W-->>F: 200 {ok: true, stored: true}
rect rgba(37, 99, 235, 0.1)
Note over W,B: Publicación en segundo plano (ctx.waitUntil),
sin duplicados por event_id y hasta 3 intentos con backoff
W->>B: publishBatch match.finished
{matchId, game, a, b, sa, sb, finishedAt}
B->>B: guarda el evento en vants_events
end
rect rgba(22, 163, 74, 0.1)
Note over A,H: flask-agents y VANTS HUB mantienen la lectura
abierta hasta 20 segundos (long-polling)
B-->>A: match.finished
B-->>H: match.finished
H->>H: notificación Resultado verificado
A->>A: redacta Resultado verificado:
equipo A marcador - marcador equipo B
A->>B: publish post.generated (channel: discord)
B-->>H: post.generated
H->>H: notificación Nueva publicación
end
rect rgba(234, 88, 12, 0.1)
Note over N,C: El cursor se deriva de max(id) de vants_feed
INSERT con ON CONFLICT DO NOTHING (idempotente)
C->>B: GET /events?since=cursor&limit=200
B-->>C: eventos nuevos del bus
C->>N: INSERT INTO vants_feed (id, type, source, ts, data)
end
```
* **Pasos 1 a 5.** vants-faceit-webhook guarda el webhook en Neon, responde `200` a FACEIT y publica `match.finished` en segundo plano. Si FACEIT repite el webhook, no se publica dos veces. Si el bus falla, lo intenta hasta 3 veces; si aun así no lo consigue, libera el `event_id` para que un nuevo envío de FACEIT pueda publicarlo.
* **Pasos 6 a 12.** VANTS HUB muestra una notificación por cada evento. flask-agents redacta el resumen (`Resultado verificado: - `) y lo publica como `post.generated`.
* **Pasos 13 a 15.** Cada 15 minutos, `vants-consumer-playvants` copia los eventos nuevos a `vants_feed`. Su cursor es el `id` más alto de esa tabla, así que si una vuelta falla, la siguiente vuelve a leer los mismos eventos sin duplicarlos. La web todavía no lee `vants_feed`.
flask-agents y VANTS HUB mantienen abierta cada lectura hasta 20 segundos (long-polling), así que reciben los eventos casi en tiempo real.
## Leer y publicar eventos
### Firma de las peticiones
Cada petición al bus lleva tres cabeceras:
| Cabecera | Valor |
| - | - |
| `X-VANTS-Project` | Nombre del proyecto, por ejemplo `flask-agents` |
| `X-VANTS-Timestamp` | Hora actual en segundos Unix |
| `X-VANTS-Signature` | `hex(hmac_sha256(secreto, "."))`. En `GET` el cuerpo está vacío y se firma `.` (con el punto final) |
### Rutas
| Método | Ruta | Uso |
| - | - | - |
| `POST` | `/events` | Publica un evento (`{"type": "...", "data": {...}}`) o varios a la vez (`{"events": [...]}`) |
| `GET` | `/events` | Devuelve los eventos posteriores al cursor `since`. Admite `types` y `sources` (separados por comas), `limit` y `wait` (segundos de espera para long-polling). La respuesta trae `events`, el `cursor` para la siguiente lectura y `hasMore` si quedan más |
### Clientes
| Cliente | Dónde está | Para |
| - | - | - |
| `vants-bus-client.js` | `vants-bus/` de Vantsgames y de vants-dev-launcher, `src/` de vants-faceit-webhook | Cloudflare Workers, Node 18 o superior y el proceso principal de Electron |
| `vants_bus_client.py` | `vants_bus/` de flask-agents | Python, solo con la biblioteca estándar |
| `vants-bus.ts` | `src/lib/bus/` de VANTSNET | TypeScript en la API de Next.js del prototipo |
```js theme={null}
import { VantsBus, EVENTS } from './vants-bus-client.js';
const bus = new VantsBus({
busUrl: env.VANTS_BUS_URL,
projectId: 'playvants-web',
secret: env.VANTS_BUS_SECRET,
});
// Publicar un evento
await bus.publish(EVENTS.CLIP_PUBLISHED, { clipId: 'abc' });
// Leer los eventos nuevos desde el último cursor guardado
const { events, cursor } = await bus.fetchEvents({
since: cursorGuardado,
types: [EVENTS.MATCH_FINISHED],
limit: 100,
});
```
> [!WARNING]
>
> El secreto solo se usa del lado del servidor: en un Worker, en el proceso principal de Electron o en un backend. No lo incluyas en el código que se envía al navegador.
> [!NOTE]
>
> Cloudflare rechaza con `403` (error 1010) los `User-Agent` genéricos, como el de `Python-urllib`. Envía uno propio, como hacen los clientes.
# Glosario
Source: https://vantsgg.mintlify.app/glosario
Significado de los términos, prefijos y nombres internos de VANTS: pt_, mm_, RR, rangos, VANTS-BUS, VANTCALL, Neon Functions, RLS, Hyperdrive y otros.
Este glosario define los nombres propios, prefijos y siglas que aparecen en el código de VANTS y en esta documentación. Cada término enlaza a la página que lo explica en detalle cuando la hay.
## Plataforma y competición
| Término | Significado |
| - | - |
| VANTS | Plataforma de esports con web de torneos, matchmaking, ranking, bot de Discord y apps de escritorio y Android. Web: [vants.vants.workers.dev](https://vants.vants.workers.dev) |
| `pt_` | Prefijo de las tablas de la plataforma de torneos: el catálogo público de juegos, torneos, equipos, partidas, jugadores y artículos. Ver [Plataforma de torneos](/referencia/esquema-base-de-datos#plataforma-de-torneos) |
| `pt_players` | Catálogo público de jugadores. Es una tabla distinta de `players`, que guarda los perfiles de la plataforma |
| `mm_` | Prefijo de las tablas del matchmaking: cola, partidas, participantes, invitaciones y confirmaciones. Ver [Matchmaking](/referencia/esquema-base-de-datos#matchmaking) |
| `duo` y `team` | Modos del matchmaking: `duo` es 1v1 (2 jugadores) y `team` es 5v5 (10 jugadores) |
| Rating | Número que mide el nivel de un jugador. Siempre queda entre 0.50 y 1.60 |
| RR | El rating multiplicado por 100. Es la cifra que ve el jugador. Ver [Historial de RR](/referencia/esquema-base-de-datos#historial-de-rr) |
| Rango | Nivel que corresponde a un rating, de menor a mayor: ACERO, TITANIO, OBSIDIANA, CROMO, PLASMA, ÉTER y VANTS. La web de vants-dev-launcher añade FUNDICIÓN por debajo de 0.95. La web de Vantsgames muestra otra escalera, VANTS RANKED, con 9 rangos por XP, de CENIZA a SUPREMO. Ver [Escala de rango](/referencia/esquema-base-de-datos#escala-de-rango) |
| Colocación | Partidas de un jugador que todavía no tiene rating. Parten de un rating de 0.95 |
| Serie ranked | Enfrentamiento entre un retador y un rival. Lo reporta un jugador y lo confirma el rival o el propietario (`series_ranked`) |
| Presencia | Quién está conectado: `online`, `jugando` o `ausente`. La web y la app de escritorio envían un latido cada 60 segundos (`presence`) |
| `is_demo` | Marca de los datos de demostración. Filtra por `is_demo = true` para ocultarlos |
| FACEIT | Plataforma externa de partidas competitivas. VANTS sincroniza sus torneos, procesa sus partidas para dar XP y rango, y recibe sus webhooks a través de FACEIT App Studio |
| VLR | vlr.gg, la web de resultados de esports de Valorant. `scripts/scraper_vlr.py` y `vlr-results/` trabajan con sus resultados |
| Propietario | El dueño de la plataforma. Recibe por mensaje directo los avisos del bot y del agente, y aprueba los cambios antes de que lleguen a `main` |
## Infraestructura
| Término | Significado |
| - | - |
| Worker `vants` | Worker de Cloudflare que sirve la web y la API pública. Su código está en `vants-server/worker.js`. Ver [Arquitectura](/arquitectura) |
| Neon | Proveedor de la base de datos Postgres de VANTS. Ver [Esquema de la base de datos](/referencia/esquema-base-de-datos) |
| Data API | API REST de Neon con la semántica de PostgREST. El Worker le reenvía el JWT de Auth0 del usuario y la RLS de cada tabla decide qué puede hacer |
| RLS | Row Level Security de Postgres: reglas por fila que deciden qué puede leer o escribir cada usuario |
| Auth0 | Proveedor de identidad. Las sesiones son JWT de Auth0 y `auth0_sub` identifica al usuario en la base de datos |
| Neon Functions | Funciones serverless de `neon-functions/`: `vantsdata`, `mm`, `vantsbot`, `linker`, `supervisor`, `discord-notify`, `stripe-checkout` y `stripe-webhook`. Ver [Mapa del código](/mapa-del-codigo#datos-y-neon-functions) |
| `vantsdata` | Neon Function de solo lectura que atiende las lecturas públicas sin sesión, como torneos, calendario y ranking |
| Hyperdrive | Configuración de Cloudflare (`vants-neon`) que agrupa y cachea conexiones a Neon. Se usa con el binding `HYPERDRIVE`. Ver [VantsBasededatos](/repos/vants-base-de-datos) |
| Rama `pr-` | Rama de Neon que se crea al abrir un PR en VantsBasededatos para probar sus migraciones sobre una copia de producción. Se borra al cerrar el PR |
| Migración | Archivo SQL numerado e idempotente de `db/migrations/` que cambia el esquema. La numeración no se reescribe. Ver [VantsBasededatos](/repos/vants-base-de-datos) |
| TWA | Trusted Web Activity: la app Android de VANTS, que muestra la web dentro de una app |
| MYKD | Identidad visual de VANTS: rojo `#ff0d2e` sobre fondo oscuro |
| `DECISIONS.md` | Registro de decisiones de arquitectura (D1, D2 y siguientes) de Vantsgames y vants-dev-launcher. Ver [Decisiones](/arquitectura#decisiones) |
## Bots, agentes y eventos
| Término | Significado |
| - | - |
| VANTCALL | Bot de Discord de VANTS, escrito en Python con `discord.py`. Ver [Bot de Discord](/referencia/bot-de-discord) |
| ORION | Nombre de VANTCALL en algunos README |
| Agente VANTS | Servicio que vigila la plataforma, registra incidencias y ejecuta órdenes. Su código está en `services/agent-service/` y guarda su trabajo en las tablas `agent_`. Ver [Agente](/referencia/esquema-base-de-datos#agente) |
| `/agente` | Comando de Discord para consultar el estado del agente y darle órdenes. Ver [Comandos](/referencia/bot-de-discord#comandos) |
| Supervisor | Recoge la telemetría de la web (errores de JavaScript, fallos de red, páginas vistas e inicios de sesión) en `web_events`. `bot/supervisor.py` la reenvía al propietario por mensaje directo |
| VANTS-BUS | Bus de eventos inmutables con el que se comunican los proyectos, por ejemplo con `match.finished`. Ver [Bus de eventos](/referencia/esquema-base-de-datos#bus-de-eventos) |
| VANTS HUB | Launcher de escritorio de VANTS hecho con Electron, en `desktop-electron/` de [vants-dev-launcher](/repos/vants-dev-launcher) |
# VANTS GAMES
Source: https://vantsgg.mintlify.app/index
Documentación interna de VANTS, plataforma de esports: qué hace cada repositorio, cómo se conectan las piezas y dónde está la referencia técnica.
VANTS GAMES es una plataforma de esports con torneos, partidas en vivo, ranking, equipos, jugadores, tienda y membresías. Usa login con **Auth0**, datos en **Neon Postgres** y despliegue en **Cloudflare Workers**. Esta documentación interna cubre siete repositorios de la organización VANTSNET en GitHub y dos de la cuenta feispla: un prototipo de estadísticas y torneos que también se llama VANTSNET, y vantsoport, el soporte de la extensión Vants para GitHub Copilot Chat.
Web: [https://vants.vants.workers.dev](https://vants.vants.workers.dev)
## Empieza aquí
> **[Mapa del código](/mapa-del-codigo)**
>
> Qué repositorio y qué archivo tocar para cada cambio.
> **[Arquitectura general](/arquitectura)**
>
> Cómo se conectan el Worker, Neon, Auth0, el bot y los despliegues.
> **[Cómo se conectan los repositorios](/conexiones-entre-repositorios)**
>
> Diagrama de repositorios y servicios, y quién publica y consume cada evento de VANTS-BUS.
> **[Glosario](/glosario)**
>
> Prefijos, siglas y nombres propios como `pt_`, RR o VANTS-BUS.
> **[Agentes de IA](/agentes)**
>
> MCP, `llms.txt` y las reglas para trabajar con agentes en el código.
## Repositorios
> **[Vantsgames](/repos/vantsgames)**
>
> Repositorio principal: Worker de Cloudflare, Neon Functions, bot de Discord, agente y workflows.
> **[vants-dev-launcher](/repos/vants-dev-launcher)**
>
> Páginas de la plataforma de torneos, home en Astro, launcher de escritorio, servidor MCP y Docker.
> **[Vants-Proteger](/repos/vants-proteger)**
>
> SPA estática con app Vue, versión anterior del bot y app Android.
> **[VantsBasededatos](/repos/vants-base-de-datos)**
>
> Migraciones SQL del esquema de Neon y una rama de pruebas por PR.
> **[vants-clips](/repos/vants-clips)**
>
> Almacén de clips destacados: vídeos, miniaturas y `manifest.json`.
> **[vants-faceit-webhook](/repos/vants-faceit-webhook)**
>
> Worker que recibe webhooks de FACEIT y los publica en el bus de eventos.
> **[flask-agents](/repos/flask-agents)**
>
> Web VANTS AI, cuartel de los agentes: squad, chat Vants Soporte con GLM y consumidor de VANTS-BUS.
> **[VANTSNET](/repos/vantsnet)**
>
> Prototipo de estadísticas de Valorant y Steam con torneos, equipos e integraciones de Discord y FACEIT.
> **[vantsoport](/repos/vantsoport)**
>
> Soporte, privacidad y términos de Vants, la extensión de GitHub Copilot Chat.
## Referencia
> **[API pública](/referencia/api-publica)**
>
> Rutas JSON, parámetros, respuestas y errores.
> **[Esquema de la base de datos](/referencia/esquema-base-de-datos)**
>
> Tablas, relaciones y estados por área.
> **[Bot de Discord](/referencia/bot-de-discord)**
>
> Comandos, permisos y alertas automáticas.
> **[Seguridad](/referencia/seguridad)**
>
> Identidad, datos, webhooks, secretos y despliegues.
> [!TIP]
>
> ¿Trabajas con un agente de IA? Conéctalo al servidor MCP `https://vantsgg.mintlify.app/mcp` o dale el índice [llms.txt](https://vantsgg.mintlify.app/llms.txt). Más opciones en [Agentes de IA](/agentes).
# Mapa del código
Source: https://vantsgg.mintlify.app/mapa-del-codigo
Qué repositorio, carpeta y archivo tocar para cada cambio en VANTS: Worker, API, Neon Functions, bot, agente, migraciones, FACEIT, apps y clips.
Esta página indica qué archivo hay que tocar para cambiar algo en VANTS y dónde está explicado. Las rutas son las de [Vantsgames](/repos/vantsgames), el repositorio principal, salvo que la fila indique otro repositorio.
## Repositorios
| Repositorio | Qué contiene | Página |
| - | - | - |
| Vantsgames | Repositorio principal: Worker `vants`, páginas de la web, Neon Functions, bot de Discord, agente, bus de eventos, scripts y workflows | [Vantsgames](/repos/vantsgames) |
| vants-dev-launcher | Comparte con Vantsgames el Worker, las Neon Functions, el bot y el agente, y añade las páginas de la plataforma de torneos, la home en Astro, el launcher de escritorio Electron, el servidor MCP y servicios en Docker | [vants-dev-launcher](/repos/vants-dev-launcher) |
| Vants-Proteger | SPA estática con una app Vue en `web/`, una versión anterior del bot de Discord y una app Android | [Vants-Proteger](/repos/vants-proteger) |
| VantsBasededatos | Migraciones SQL del esquema de Neon y su flujo de pruebas con una rama por PR | [VantsBasededatos](/repos/vants-base-de-datos) |
| vants-clips | Clips destacados: cada clip es un release con vídeo y miniatura, y `manifest.json` es el índice. Solo datos, sin código | [vants-clips](/repos/vants-clips) |
| vants-faceit-webhook | Worker que recibe los webhooks de FACEIT, los guarda en Neon y los publica en VANTS-BUS | [vants-faceit-webhook](/repos/vants-faceit-webhook) |
| flask-agents | Web VANTS AI, cuartel general de los agentes, en Flask sobre Vercel: presenta el squad, sirve el chat Vants Soporte con GLM y tiene el consumidor de VANTS-BUS, que publica como `post.generated` los textos que redacta sobre partidos y torneos | [flask-agents](/repos/flask-agents) |
| VANTSNET | Prototipo de red de estadísticas y plataforma competitiva en la cuenta feispla: Next.js 16 y Prisma ORM 8 con PostgreSQL, Valorant con la API de Riot, Steam, torneos y equipos, Discord y FACEIT. Publica sus eventos en VANTS-BUS y no comparte base de datos con los demás | [VANTSNET](/repos/vantsnet) |
| vantsoport | Soporte, privacidad y términos de Vants, la extensión de GitHub Copilot Chat, en la cuenta feispla. Sin código y sin relación con la plataforma | [vantsoport](/repos/vantsoport) |
## Web y API
| Qué quieres cambiar | Dónde | Más información |
| - | - | - |
| Rutas de la web y de la API, cabeceras de seguridad, protección contra bots y límites de uso | `vants-server/worker.js` | [Seguridad](/referencia/seguridad#la-web-y-la-api) |
| Validación de parámetros, orden, paginación y formato de las respuestas de la API | `vants-server/plataforma-core.js` | [Reglas generales](/referencia/api-publica#reglas-generales) |
| Configuración del Worker `vants` | `vants-server/wrangler.toml` | [Despliegue](/repos/vantsgames#despliegue) |
| Páginas HTML de la web | `vants-server/public/` | [Rutas de la web](/referencia/api-publica#rutas-de-la-web) |
| JavaScript y CSS de la web | `vants-server/public/static/` | |
| Pruebas de la API, del matchmaking, del agente y de `vantsbot` | `scripts/test_*.js` | [Workflows](/repos/vantsgames#workflows) |
| Pruebas end-to-end en navegador (Playwright) | `tests/e2e/` | |
> [!WARNING]
>
> En vants-dev-launcher la web estática está repetida en tres carpetas que deben ser idénticas: la raíz, `public/` y `vants-server/public/`. Un cambio en una se aplica en las tres. `node scripts/build-webapp.mjs` copia el build de `webapp/` a las tres y `node scripts/build-webapp.mjs --check` comprueba que siguen iguales; el workflow `fullstack` lo vigila.
## Datos y Neon Functions
Cada función vive en `neon-functions//index.ts`.
| Qué quieres cambiar | Dónde | Más información |
| - | - | - |
| Tablas, columnas, vistas y funciones SQL | `db/migrations/` de VantsBasededatos | [Esquema](/referencia/esquema-base-de-datos), [VantsBasededatos](/repos/vants-base-de-datos) |
| Lecturas públicas sin sesión: torneos, calendario, ranking y reglas | `neon-functions/vantsdata/` | [API pública](/referencia/api-publica) |
| Matchmaking: cola, partidas, reportes, historial y RR | `neon-functions/mm/` | [Matchmaking](/referencia/esquema-base-de-datos#matchmaking) |
| Vincular cuentas de otras plataformas (OAuth) | `neon-functions/linker/` | |
| Órdenes al agente desde Discord (`/agente`) | `neon-functions/vantsbot/` | [Comandos](/referencia/bot-de-discord#comandos) |
| Telemetría de la web: errores, fallos de red, páginas vistas e inicios de sesión | `neon-functions/supervisor/` | [Bot y eventos web](/referencia/esquema-base-de-datos#bot-y-eventos-web) |
| Avisos de la plataforma en Discord | `neon-functions/discord-notify/` | |
| Pagos con Stripe | `neon-functions/stripe-checkout/`, `neon-functions/stripe-webhook/` | [Webhooks](/referencia/seguridad#webhooks) |
| Eventos entre proyectos (VANTS-BUS) | `vants-bus/` | [Bus de eventos](/referencia/esquema-base-de-datos#bus-de-eventos), [Conexiones](/conexiones-entre-repositorios#vants-bus) |
| Copia de los eventos del bus a `vants_feed` (Worker `vants-consumer-playvants`) | `vants-bus/consumer-worker/` | [Conexiones](/conexiones-entre-repositorios#vants-bus) |
## Bot, agente y automatizaciones
| Qué quieres cambiar | Dónde | Más información |
| - | - | - |
| Comandos del bot de Discord | `bot/commands/` | [Comandos](/referencia/bot-de-discord#comandos) |
| Alertas automáticas del bot | `bot/alerts.py` | [Bot de Discord](/referencia/bot-de-discord) |
| Avisos de telemetría al propietario por mensaje directo | `bot/supervisor.py` | [Bot de Discord](/referencia/bot-de-discord) |
| Respuestas del agente por mensaje directo | `bot/agent_bridge.py` | [Bot de Discord](/referencia/bot-de-discord) |
| Agente VANTS: chequeos, acciones, memoria y modelo de lenguaje | `services/agent-service/` | [Agente](/referencia/esquema-base-de-datos#agente) |
| Sincronización diaria de torneos de FACEIT | `scripts/sincronizar-faceit.mjs` (workflow `faceit-sync`) | [Plataforma de torneos](/referencia/esquema-base-de-datos#plataforma-de-torneos) |
| XP y rango de las partidas de FACEIT | `scripts/faceit-xp.mjs` (workflow `faceit-xp`) | [Escala de rango](/referencia/esquema-base-de-datos#escala-de-rango) |
| Aviso en Discord de cada push a `main` | `scripts/notificar-discord.mjs` (workflow `mejoras-discord`) | |
| Monitor de disponibilidad | `monitor/monitor.js` (workflow `monitor`) | |
| Resultados de vlr.gg | `scripts/scraper_vlr.py`, `vlr-results/` | |
| Cuándo se ejecuta cada workflow | `.github/workflows/` | [Workflows](/repos/vantsgames#workflows) |
## Otros repositorios
| Qué quieres cambiar | Dónde | Más información |
| - | - | - |
| Recepción de webhooks de FACEIT | vants-faceit-webhook, `src/index.js` | [vants-faceit-webhook](/repos/vants-faceit-webhook) |
| Conversión de webhooks de FACEIT en eventos del bus | vants-faceit-webhook, `src/vants-bus-faceit.js` | [vants-faceit-webhook](/repos/vants-faceit-webhook) |
| Reacciones de los agentes a los eventos del bus | flask-agents, `vants_bus/consumer.py` | [flask-agents](/repos/flask-agents#reacciones) |
| Notificaciones de VANTS-BUS en el launcher | vants-dev-launcher, `desktop-electron/vants-bus-consumer.js` | [Conexiones](/conexiones-entre-repositorios#vants-bus) |
| Publicación de eventos desde el Worker de la web | Vantsgames, `vants-server/worker.js` con el cliente de `vants-bus/` | [Conexiones](/conexiones-entre-repositorios#vants-bus) |
| Publicación de eventos desde VANTSNET | VANTSNET, `src/lib/bus/` | [VANTSNET](/repos/vantsnet#publicación-en-vants-bus) |
| Clips destacados | La web los publica con `/api/clips/featured` y se guardan en vants-clips | [Clips destacados](/referencia/api-publica#clips-destacados) |
| Home en Astro con islas React y Vue | vants-dev-launcher, `webapp/` | [vants-dev-launcher](/repos/vants-dev-launcher) |
| Launcher de escritorio | vants-dev-launcher, `desktop-electron/` | [vants-dev-launcher](/repos/vants-dev-launcher) |
| Servidor MCP del proyecto | vants-dev-launcher, `mcp/server.mjs` | [vants-dev-launcher](/repos/vants-dev-launcher) |
| Estadísticas de Valorant: perfil, historial y scoreboard | VANTSNET, `src/lib/valorant/riot.ts` y `src/app/api/valorant/` | [VANTSNET](/repos/vantsnet) |
| Perfiles de Steam | VANTSNET, `src/lib/steam/steam.ts` y `src/app/api/steam/resolve/` | [VANTSNET](/repos/vantsnet#endpoints) |
| Torneos, inscripciones y partidas de VANTSNET | VANTSNET, `src/app/api/tournaments/` y `src/lib/validators.ts` | [VANTSNET](/repos/vantsnet#plataforma-competitiva) |
| Equipos y miembros de VANTSNET | VANTSNET, `src/app/api/teams/` | [VANTSNET](/repos/vantsnet#plataforma-competitiva) |
| Vinculación con Discord y consulta de FACEIT en VANTSNET | VANTSNET, `src/lib/discord.ts`, `src/lib/faceit.ts`, `src/app/api/auth/discord/` y `src/app/api/faceit/` | [VANTSNET](/repos/vantsnet#discord-y-faceit) |
| App Android | `android/` | [Arquitectura](/arquitectura) |
| Decisiones de arquitectura | `DECISIONS.md` | [Decisiones](/arquitectura#decisiones) |
Antes de cambiar código como agente de IA, lee las reglas de trabajo en [Documentación para agentes de IA](/agentes).
# API pública
Source: https://vantsgg.mintlify.app/referencia/api-publica
Rutas, parámetros, respuestas y errores de la API JSON de VANTS: torneos, partidas, equipos, jugadores, artículos, búsqueda, clips e inscripciones.
La web de VANTS expone una API JSON de lectura bajo `/api/`. Es la misma que usa la propia web para mostrar torneos, partidas, equipos y artículos. Los ejemplos usan el dominio de producción, `https://vants.vants.workers.dev`. El código está en `vants-server/worker.js` (rutas) y `vants-server/plataforma-core.js` (validación, orden y paginación) de [Vantsgames](/repos/vantsgames).
> [!NOTE]
>
> Los registros con `is_demo: true` son datos de demostración. Su nombre empieza por `DEMO ·` y sirven para validar la plataforma.
## Reglas generales
| Regla | Detalle |
| - | - |
| Método | Solo `GET`. Cualquier otro método recibe `405`. |
| Formato | JSON en UTF-8. |
| Origen | Si la petición trae una cabecera `Origin` que no es de un dominio de VANTS, recibe `403`. Las peticiones sin `Origin` (curl, scripts, servidores) se aceptan. |
| User-Agent | Envía siempre un `User-Agent` descriptivo. Sin él, la protección contra bots responde `403`. |
| Límite de uso | Hay un límite de peticiones por IP. Al superarlo recibes `429` y la cabecera `Retry-After` con los segundos de espera. |
| Caché | Las respuestas correctas llevan `Cache-Control: public, max-age=60`. Los errores llevan `no-store`. |
```bash theme={null}
curl -s "https://vants.vants.workers.dev/api/games" \
-H "User-Agent: mi-app/1.0"
```
## Formato de respuesta
Una respuesta correcta lleva `ok: true` y los datos en `data`:
```json theme={null}
{ "ok": true, "data": [] }
```
Un error lleva `ok: false` y un texto en `error.message`:
```json theme={null}
{ "ok": false, "error": { "message": "estado debe ser upcoming, ongoing o archive" } }
```
| Código | Cuándo ocurre |
| - | - |
| `200` | La petición se resolvió. |
| `400` | Parámetro inválido. Hoy también lo devuelve un equipo o jugador que no existe: distingue el caso con `error.message`. |
| `403` | `Origin` no permitido, o `User-Agent` ausente o bloqueado. |
| `404` | El torneo pedido no existe, o la ruta no existe. |
| `405` | Método distinto de `GET`. |
| `429` | Superaste el límite de peticiones. Espera los segundos de `Retry-After`. |
| `500` | Error interno. |
> [!NOTE]
>
> Si el origen de datos no responde, recibes `400` con el mensaje `Datos no disponibles (proxy )`. Reintenta pasado un momento.
## Rutas de lectura
| Ruta | Descripción |
| - | - |
| `GET /api/games` | Juegos activos. |
| `GET /api/tournaments` | Lista paginada de torneos por estado. |
| `GET /api/tournaments/:slug` | Ficha de un torneo con sus equipos confirmados. |
| `GET /api/matches` | Lista paginada de partidas por estado. |
| `GET /api/teams` | Lista de equipos, hasta 50. |
| `GET /api/teams/:slug` | Ficha de un equipo con plantilla y partidas. |
| `GET /api/players` | Ranking de perfiles VANTS, 50 por página. |
| `GET /api/players/:slug` | Ficha de un jugador de la plataforma de torneos. |
| `GET /api/articles` | Artículos publicados, 20 por página. |
| `GET /api/search` | Búsqueda en torneos, equipos, artículos y jugadores. |
Los slugs usan letras minúsculas, números y guiones. Los de equipo y jugador tienen entre 2 y 64 caracteres.
### Juegos
`GET /api/games` no admite parámetros. Usa el campo `slug` como valor del parámetro `juego` en el resto de rutas.
```json theme={null}
{
"ok": true,
"data": [
{ "slug": "valorant", "nombre": "VALORANT", "corto": "VALORANT", "logo_url": null, "color": null, "activo": true },
{ "slug": "cs2", "nombre": "COUNTER-STRIKE 2", "corto": "CS2", "logo_url": null, "color": null, "activo": true }
]
}
```
### Torneos
`GET /api/tournaments`
| Parámetro | Por defecto | Descripción |
| - | - | - |
| `estado` | `upcoming` | `upcoming` (próximos), `ongoing` (en curso) o `archive` (finalizados). |
| `juego` | todos | Slug de un juego, por ejemplo `valorant`. |
| `region` | todas | Región de 2 a 8 letras o números, por ejemplo `LATAM`. No distingue mayúsculas. |
| `pag` | `1` | Página, de 1 a 500. |
| `porPagina` | `12` | Resultados por página, de 1 a 50. |
Un número fuera de rango se ajusta al límite más cercano. Un `estado`, `juego` o `region` inválido devuelve `400`. Los próximos y en curso se ordenan por fecha de inicio ascendente. Los finalizados, descendente.
```bash theme={null}
curl -s "https://vants.vants.workers.dev/api/tournaments?estado=upcoming&juego=valorant&porPagina=5" \
-H "User-Agent: mi-app/1.0"
```
```json theme={null}
{
"ok": true,
"data": {
"pag": 1,
"porPagina": 5,
"total": 1,
"items": [
{
"slug": "demo-copa-comunidad-s5",
"nombre": "DEMO · Copa Comunidad S5",
"descripcion": "Torneo de demostración para validar la plataforma.",
"game": { "slug": "valorant", "nombre": "VALORANT" },
"estado": "upcoming",
"major": false,
"region": "LATAM",
"location_type": "online",
"prize": { "value": 500, "currency": "USD" },
"fee": 0,
"max_teams": 16,
"starts_at": "2026-11-08T15:39:27.319Z",
"ends_at": "2026-11-12T15:39:27.319Z",
"logo_url": null,
"formato": "Eliminación simple · Bo1",
"source": "demo",
"is_demo": true
}
]
}
}
```
| Campo | Descripción |
| - | - |
| `slug` | Identificador del torneo para URLs. |
| `nombre`, `descripcion` | Textos del torneo. `descripcion` puede ser `null`. |
| `game` | Objeto con el `slug` y el `nombre` del juego. |
| `estado` | `upcoming`, `ongoing`, `finished` o `cancelled`. |
| `major` | `true` si es un torneo major. |
| `region` | Región, por ejemplo `LATAM`, o `null`. |
| `location_type` | `online`, `lan` o `null`. |
| `prize` | Objeto con `value` y `currency`, o `null` si no hay premio. |
| `fee` | Cuota de inscripción. `0` si es gratis. |
| `max_teams` | Máximo de equipos, o `null`. |
| `starts_at`, `ends_at` | Fechas en formato ISO 8601 (UTC), o `null`. |
| `logo_url` | URL del logo, o `null`. |
| `formato` | Descripción del formato, o `null`. |
| `source` | Origen del dato: `vants`, `faceit` o `demo`. |
| `is_demo` | `true` si es un dato de demostración. |
> [!NOTE]
>
> El filtro usa `archive`, pero el campo `estado` de un torneo finalizado devuelve `finished`. Además, en torneos y partidas el campo `pag` de la respuesta devuelve hoy `1` aunque pidas otra página. Usa el valor que enviaste. El campo `total` sí es el total de resultados.
#### Ficha de un torneo
`GET /api/tournaments/:slug` devuelve los mismos campos que la lista y añade `participantes`: los equipos con inscripción confirmada.
```json theme={null}
{
"ok": true,
"data": {
"slug": "demo-copa-comunidad-s5",
"nombre": "DEMO · Copa Comunidad S5",
"estado": "upcoming",
"participantes": [
{ "name": "Equipo Ejemplo", "tag": "EJE", "captain": "Capitán", "region": "LATAM", "game": "valorant" }
]
}
}
```
El ejemplo omite el resto de campos del torneo. Si el slug no existe, responde `404` con el mensaje `Torneo no encontrado`.
### Partidas
`GET /api/matches`
| Parámetro | Por defecto | Descripción |
| - | - | - |
| `estado` | `upcoming` | `live` (en vivo), `upcoming` (próximas) o `finished` (terminadas). |
| `juego` | todos | Slug de un juego. |
| `pag` | `1` | Página, de 1 a 500. |
| `porPagina` | `12` | Resultados por página, de 1 a 50. |
Las próximas se ordenan de la más cercana a la más lejana. Las demás, de la más reciente a la más antigua.
```json theme={null}
{
"ok": true,
"data": {
"pag": 1,
"porPagina": 12,
"total": 1,
"items": [
{
"game": { "slug": "valorant", "nombre": "VALORANT" },
"tournament_slug": "6f1f2b0e-1c4d-4e57-9a3b-0d2c5e8a7b11",
"equipos": { "a": "DEMO · Equipo Lobo", "b": "DEMO · Equipo Fénix" },
"score": null,
"estado": "upcoming",
"best_of": 1,
"empieza_at": "2026-10-19T17:39:27.392Z",
"is_demo": true
}
]
}
}
```
| Campo | Descripción |
| - | - |
| `equipos` | Nombres de los dos equipos, `a` y `b`. |
| `score` | Objeto con `a` y `b` cuando hay resultado, o `null`. |
| `estado` | `upcoming`, `live`, `finished` o `cancelled`. |
| `best_of` | Serie al mejor de N mapas (por ejemplo `3` para Bo3), o `null`. |
| `empieza_at` | Fecha de inicio en formato ISO 8601 (UTC), o `null`. |
| `tournament_slug` | Hoy contiene el identificador interno (UUID) del torneo, no su slug. No sirve para pedir `/api/tournaments/:slug`. |
### Equipos
`GET /api/teams` devuelve hasta 50 equipos ordenados por nombre, sin paginación. Acepta el parámetro opcional `juego`.
```json theme={null}
{
"ok": true,
"data": [
{ "slug": "demo-equipo-fenix", "nombre": "DEMO · Equipo Fénix", "tag": "FNX", "game": "valorant", "region": "LATAM", "logo_url": null, "is_demo": true }
]
}
```
`GET /api/teams/:slug` devuelve la ficha del equipo con dos listas añadidas:
* `plantilla`: hasta 30 jugadores, ordenados por nombre.
* `partidas`: las más recientes primero, hasta 20 como equipo A y 20 como equipo B.
```json theme={null}
{
"ok": true,
"data": {
"slug": "demo-equipo-fenix",
"nombre": "DEMO · Equipo Fénix",
"tag": "FNX",
"game": { "slug": "valorant", "nombre": "VALORANT" },
"region": "LATAM",
"logo_url": null,
"source": null,
"is_demo": true,
"plantilla": [
{
"slug": "demo-jugador-alex",
"nombre": "DEMO · Alex",
"game": { "slug": "valorant", "nombre": "VALORANT" },
"team_slug": "demo-equipo-fenix",
"rol": "IGL",
"pais": "VE",
"bio": null,
"source": "demo",
"is_demo": true
}
],
"partidas": []
}
}
```
> [!NOTE]
>
> En la lista, `game` es el slug del juego (texto). En la ficha, `game` es un objeto con `slug` y `nombre`.
### Jugadores
`GET /api/players` devuelve los perfiles VANTS ordenados por `rating` de mayor a menor, 50 por página. Acepta `pag` (de 1 a 500). Solo expone campos públicos: nunca incluye correo ni datos de contacto o de autenticación.
```json theme={null}
{
"ok": true,
"data": {
"pag": 1,
"items": [
{
"id": "b1c2d3e4-0000-4000-8000-000000000001",
"role": "jugador",
"is_demo": false,
"display_name": "NickEjemplo",
"rating": 0.72,
"region": "LATAM"
}
],
"total": 1
}
}
```
`GET /api/players/:slug` devuelve la ficha de un jugador de la plataforma de torneos: `slug`, `nombre`, `game`, `team_slug`, `rol`, `pais`, `bio`, `source` e `is_demo`. Si el jugador tiene equipo, añade `equipo` con los datos de ese equipo, con el mismo formato que la ficha de equipo sin las listas.
> [!NOTE]
>
> La lista sale de los perfiles VANTS y la ficha por slug, de los jugadores de la plataforma de torneos. Son conjuntos distintos, así que un perfil de la lista puede no tener ficha.
### Artículos
`GET /api/articles` devuelve los artículos publicados, del más reciente al más antiguo, 20 por página.
| Parámetro | Por defecto | Descripción |
| - | - | - |
| `pag` | `1` | Página, de 1 a 500. |
| `juego` | todos | Slug de un juego. |
```json theme={null}
{
"ok": true,
"data": {
"pag": 1,
"items": [
{
"slug": "demo-calendario-s5",
"titulo": "DEMO · Calendario de la Temporada 5",
"resumen": "Artículo de demostración con el calendario previsto de la temporada.",
"autor": "VANTS",
"game": "cs2",
"publicado_at": "2026-10-09T16:10:57.064Z",
"is_demo": true
}
],
"total": 1
}
}
```
La lista no incluye el cuerpo del artículo. La ficha de un artículo no está disponible por API todavía.
### Búsqueda
`GET /api/search?q=texto` busca en torneos, equipos, artículos y jugadores. Devuelve hasta 8 resultados por tipo.
| Parámetro | Descripción |
| - | - |
| `q` | Obligatorio. Entre 2 y 64 caracteres. No admite `%`, `_`, `;` ni la barra invertida. |
```bash theme={null}
curl -s "https://vants.vants.workers.dev/api/search?q=lobo" \
-H "User-Agent: mi-app/1.0"
```
```json theme={null}
{
"ok": true,
"data": {
"q": "lobo",
"torneos": [],
"equipos": [
{ "slug": "demo-equipo-lobo", "nombre": "DEMO · Equipo Lobo", "tag": "LBO", "game_slug": "valorant", "is_demo": true }
],
"articulos": [],
"jugadores": []
}
}
```
La búsqueda de `jugadores` cubre un conjunto limitado de perfiles, así que no es exhaustiva.
## Rutas de la web
La web usa además rutas propias bajo `/api/`. Las que piden sesión necesitan la cabecera `Authorization: Bearer ` de un usuario con la sesión iniciada y un `Origin` de un dominio de VANTS. Sus errores tienen la forma `{ "ok": false, "error": "codigo" }`, distinta de la de las rutas de lectura.
| Ruta | Método | Sesión | Descripción |
| - | - | - | - |
| `/api/health` y `/healthz` | GET | No | Comprobación de estado del servicio. |
| `/api/en-vivo` | GET | No | Jugadores conectados ahora (presencia de los últimos 2 minutos), hasta 50. Caché de 10 s. Si la base no responde devuelve `ok: false` y `jugadores: []`. |
| `/api/torneos/bracket?torneo=` | GET | No | Equipos y cuadro de un torneo de la web. `id`: de 1 a 40 letras, números, `_` o `-`. Caché de 15 s. Sin datos devuelve `ok: false` con `motivo: sin_datos`. |
| `/api/valorant/esports` | GET | No | Calendario de esports de VALORANT desde un proveedor externo. Caché de 5 minutos. Puede devolver `503`. |
| `/api/gpr` | GET | No | Global Power Rankings de Riot: `calculated`, `total` y `regions`. Caché de 1 hora. Devuelve `502` si falla la fuente. |
| `/api/faceit/usuario?nick=` | GET | No | Perfil FACEIT: id, nickname, país, avatar, nivel, región, elo y fecha de alta. Caché de 60 s. |
| `/api/faceit/historia?nick=` | GET | No | Hasta 10 partidas recientes de CS2 y un `resumen` con jugadas, victorias, winrate, K/D, HS, MVP, nivel y elo. Caché de 120 s. |
| `/api/teams-live` | GET | No | Heredada. Equipos inscritos y campeones. Úsala solo si ya dependes de ella. |
| `/api/clips/featured` | GET | No | Clips destacados. Caché de 60 s. |
| `/api/clips/featured` | POST | Sí | Sube un clip. |
| `/api/clips/featured?id=` | DELETE | Sí | Borra un clip. Solo puede hacerlo su autor. |
| `/api/torneos/inscribir` | POST | Sí | Inscribe un equipo en un torneo. |
| `/api/account/perfil` | GET, POST | Sí | Lee y actualiza el nick de la cuenta y las redes conectadas. |
El parámetro `nick` de FACEIT tiene entre 2 y 24 caracteres: letras, números, `_`, `.` o `-`. Un nick inválido devuelve `400` con `nick_invalido`, y uno que no existe, `404` con `no_encontrado`.
### Clips destacados
`GET /api/clips/featured` devuelve `clips`, hasta 60 entradas. Cada clip incluye, entre otros, `id`, `nick`, `username`, `titulo`, `juego`, `ext`, `fecha`, `dur`, `tam`, `video` y `thumb`. Los vídeos, las miniaturas y el índice se guardan en [vants-clips](/repos/vants-clips).
`POST` recibe un formulario `multipart/form-data` con estos campos: `video`, `thumb`, `titulo`, `juego`, `nick`, `username`, `dur` y `ext`.
| Límite | Valor |
| - | - |
| Tamaño del vídeo | 48 MB |
| Tamaño de la miniatura | 400 KB |
| Extensiones | `webm`, `mp4`, `mkv`, `mov` y `m4v` |
| Clips guardados | 60. Al superarlo, se descartan los más antiguos. |
Si el `id` de un `DELETE` no existe devuelve `404` con `no_existe`, y si el clip es de otro usuario, `403` con `no_es_tuyo`.
### Inscribir un equipo
`POST /api/torneos/inscribir` crea una inscripción con estado `pendiente`. Recibe un cuerpo JSON:
| Campo | Obligatorio | Descripción |
| - | - | - |
| `torneo` | Sí | Identificador del torneo: de 1 a 40 letras, números, `_` o `-`. |
| `juego` | No | `cs2` o `valorant`. Cualquier otro valor se trata como `valorant`. |
| `equipo` | Sí | Nombre del equipo, de 2 a 32 caracteres. Se limpian `<`, `>` y las comillas invertidas. |
| `tag` | Sí | Etiqueta de 2 a 4 caracteres alfanuméricos. Se descartan los demás. |
| `capitan` | No | Nombre del capitán, hasta 40 caracteres. |
| `region` | No | Región, hasta 40 caracteres. |
Si todo va bien, responde `200` con `ok: true` y, en `equipo`, el registro creado con `status` igual a `pendiente`. Estos son los errores posibles:
| Código | `error` | Causa |
| - | - | - |
| `400` | `torneo_invalido` | `torneo` no cumple el formato. |
| `400` | `datos_invalidos` | `equipo` o `tag` quedan con menos de 2 caracteres. |
| `401` | `sin_sesion` o `token_invalido` | Falta el token o no es válido. |
| `403` | `Origen no permitido` | El `Origin` falta o no es de un dominio de VANTS. |
| `405` | `Solo POST` | Método distinto de `POST`. |
| `409` | `equipo_ya_inscrito` | Ese equipo ya está inscrito en el torneo. |
| `429` | `Demasiadas peticiones` | Superaste el límite de peticiones. |
| `502` | `base_inaccesible` o `base_rechazo` | La base de datos no respondió o rechazó el registro. |
## Más información
* [Vantsgames](/repos/vantsgames): estructura del Worker y despliegue.
* [Seguridad](/referencia/seguridad): protecciones de la API y de la plataforma.
* [Esquema de la base de datos](/referencia/esquema-base-de-datos): tablas que alimentan esta API.
* [Arquitectura](/arquitectura): cómo encajan los componentes.
# Bot de Discord
Source: https://vantsgg.mintlify.app/referencia/bot-de-discord
Comandos, permisos, alertas automáticas, puesta en marcha y tablas de VANTCALL, el bot de Discord de VANTS escrito en Python con discord.py.
VANTCALL es el bot de Discord de VANTS (en algunos README aparece como ORION). Consulta perfiles y rankings, ayuda a moderar, publica anuncios y avisa de torneos, eventos y resultados nuevos. Está escrito en Python con `discord.py` y usa la misma base de datos que la plataforma, descrita en el [esquema](/referencia/esquema-base-de-datos).
Su código está en la carpeta `bot/` de [Vantsgames](/repos/vantsgames).
> [!NOTE]
>
> Esta página describe la versión de Vantsgames. [vants-dev-launcher](/repos/vants-dev-launcher) incluye una copia con la misma estructura y [Vants-Proteger](/repos/vants-proteger) una versión anterior, sin `/agente`, supervisor ni puente del agente.
## Cómo funciona
El bot mantiene una conexión permanente con Discord (gateway) y registra sus comandos solo en el servidor indicado en `DISCORD_GUILD_ID`. Al arrancar sincroniza los comandos y lanza cuatro tareas en segundo plano.
```mermaid theme={null}
flowchart LR
D[Discord] <-->|gateway| B[Bot VANTCALL]
B <--> N[(Neon Postgres)]
B -->|alertas y anuncios| C[Canales del servidor]
B -->|avisos| O[Mensaje directo al propietario]
W[Web de VANTS] --> N
A[Agente VANTS] --> N
```
| Tarea | Qué hace |
| - | - |
| Health check | Responde `ok` en `GET /` (puerto `PORT`, 8080 por defecto) para que el alojamiento compruebe que el bot sigue vivo. |
| Alertas | Cada 30 segundos publica los torneos, eventos y resultados ranked nuevos en el canal de cada categoría. |
| Supervisor | Reenvía al propietario, por mensaje directo, los eventos que captura la web, como errores y fallos de red. |
| Puente del agente | Entrega al propietario, por mensaje directo, los incidentes del agente y las respuestas a las órdenes que quedan en cola. |
El bot no se despliega con el Worker de Cloudflare: necesita un alojamiento que lo mantenga encendido, por ejemplo Railway o Render.
## Puesta en marcha
**1. Prepara la aplicación en Discord**
En el [portal de desarrolladores](https://discord.com/developers/applications) abre la aplicación del bot. Si no tienes token, usa **Bot → Reset Token** y guárdalo solo en las variables de entorno del alojamiento.
**2. Invita el bot al servidor**
Concédele los permisos de la tabla siguiente. Cada comando de moderación solo funciona si el bot tiene el permiso que necesita.
**3. Deja vacía la URL de interacciones**
El campo **Interactions Endpoint URL** debe quedar vacío: el bot recibe los comandos por gateway. Usa los intents por defecto, así que no necesitas activar ningún intent privilegiado.
**4. Configura las variables y arranca**
Las variables de entorno y los comandos de arranque están en la sección [Bot de Discord](/repos/vantsgames#bot-de-discord) de Vantsgames.
| Permiso de Discord | Para qué |
| - | - |
| View Channels, Send Messages, Embed Links | Responder y publicar alertas y anuncios |
| Read Message History | `/actividad`, `/top` y `/clear` |
| Kick Members | `/kick` |
| Ban Members | `/ban` |
| Moderate Members | `/mute` y `/unmute` |
| Manage Messages | `/clear` |
| Manage Roles | `/lock` y `/unlock` |
> [!WARNING]
>
> No ejecutes `bot/register_commands.py`. Reemplaza todos los comandos del servidor por una lista antigua. El bot registra los suyos al arrancar.
## Comandos
Todos son comandos de barra (`/`) registrados en el servidor de VANTS. La columna **Quién** indica quién puede ejecutarlos:
* **Todos**: cualquier miembro que vea el comando.
* **Staff**: administradores del servidor y miembros con un rol de staff, que el bot reconoce por el nombre del rol. Los demás reciben un mensaje de error que solo ven ellos.
* **Administrador**: solo quien tiene el permiso de administrador.
Puedes limitar cualquier comando a roles o canales concretos en **Ajustes del servidor → Integraciones**.
### Perfil y cuenta
| Comando | Quién | Qué hace |
| - | - | - |
| `/perfil [usuario]` | Todos | Perfil VANTS de un jugador: rango, puntos, número de torneos, país y cuentas de Riot y Steam vinculadas, con botones de enlace. La respuesta es pública. Si el usuario no tiene cuenta vinculada, devuelve un error privado. |
| `/verificar` | Todos | Indica, en privado, si tu Discord está vinculado a un jugador VANTS y con qué rol (administrador o jugador). Si no lo está, te pide vincularlo en la web. |
| `/vincular riot handle` | Todos | Guarda tu Riot ID como cuenta sin verificar. Tu Discord debe estar vinculado antes a una cuenta VANTS. La respuesta es privada. |
El Riot ID debe tener el formato `Nombre#TAG`: un nombre de 3 a 16 caracteres sin espacios ni `#` y una etiqueta de 3 a 5 caracteres alfanuméricos. Si ese Riot ID ya está vinculado a otra cuenta, el comando falla.
> [!NOTE]
>
> `/perfil` muestra en el canal el Riot ID y el enlace de Steam del jugador consultado.
### Valorant
| Comando | Quién | Qué hace |
| - | - | - |
| `/valorant ranking` | Todos | Top 10 de la temporada activa (o de la más reciente, si no hay ninguna activa) con posición, nombre, rango, MMR, victorias y porcentaje de victorias. La respuesta es pública. |
| `/valorant perfil [usuario]` | Todos | Rango actual y MMR, posición, victorias y derrotas, partidas y región. La respuesta es privada. |
### Estadísticas e informes
| Comando | Quién | Qué hace |
| - | - | - |
| `/stats_servidor` | Todos | Miembros de Discord, jugadores registrados, torneos, partidas ranked, canales y roles. |
| `/stats_jugador [usuario]` | Todos | Usuario, juego principal, región, rango, MMR y balance de victorias y derrotas. |
| `/actividad [dias]` | Todos | Mensajes totales y los 5 canales más activos del período (7 días por defecto). |
| `/top [dias]` | Todos | Los 10 miembros más activos del período (7 días por defecto). |
| `/informe` | Todos | Jugadores nuevos y partidas ranked de la semana, torneos activos y en curso, y top 5 ranked. |
| `/resumen` | Todos | Jugadores, torneos, partidas ranked, eventos próximos y temporada actual. |
Las respuestas de estos comandos son públicas. `/actividad` y `/top` recorren el historial de los canales de texto que el bot puede leer, así que con períodos largos pueden tardar mucho.
### Moderación
| Comando | Quién | Qué hace |
| - | - | - |
| `/warn usuario razon` | Staff | Envía una advertencia por mensaje directo al usuario y publica un aviso en el canal. No guarda ningún historial. |
| `/kick usuario razon` | Staff | Expulsa al usuario del servidor. |
| `/ban usuario razon [borrar_dias]` | Staff | Banea al usuario y borra sus mensajes de los últimos `borrar_dias` días (de 0 a 7). |
| `/mute usuario minutos razon` | Staff | Silencia al usuario con un timeout de Discord durante los minutos indicados. |
| `/unmute usuario` | Staff | Quita el silencio. |
| `/clear cantidad` | Staff | Borra hasta 100 mensajes del canal actual. |
| `/lock` | Staff | Impide que `@everyone` escriba en el canal actual. |
| `/unlock` | Staff | Devuelve a `@everyone` el permiso de escribir por defecto del canal actual. |
Las respuestas se publican en el canal, salvo la de `/clear`, que es privada. Si un comando de moderación no responde, comprueba que el rol del bot esté por encima del rol del usuario y que tenga el permiso correspondiente.
### Comunidad
| Comando | Quién | Qué hace |
| - | - | - |
| `/anuncio titulo mensaje [imagen]` | Staff | Publica un anuncio en el canal de anuncios. `imagen` es una URL opcional. |
| `/evento_crear titulo tipo fecha [descripcion]` | Staff | Crea un evento. `tipo` es texto libre; los valores previstos son `torneo`, `evento` y `scrim`. `fecha` usa el formato `YYYY-MM-DD` o `YYYY-MM-DD HH:MM`, en UTC. |
| `/torneo_crear nombre formato fecha_inicio [max_participantes]` | Staff | Crea un torneo con la inscripción abierta. `formato` es texto libre; los valores previstos son `single_elimination`, `double_elimination` y `round_robin`. `fecha_inicio` usa el formato `YYYY-MM-DD`, en UTC, y `max_participantes` vale 16 por defecto. Si no se puede crear (por ejemplo, porque ya existe un torneo con ese nombre), el bot lo avisa. |
| `/recordatorio mensaje minutos` | Staff | Publica el mensaje en el canal actual cuando pasan los minutos indicados. Si el bot se reinicia antes, el recordatorio se pierde. |
Las confirmaciones de `/anuncio`, `/evento_crear` y `/torneo_crear` son privadas. Los eventos y torneos que se crean aquí disparan las [alertas automáticas](#alertas-automáticas).
### Configuración
| Comando | Quién | Qué hace |
| - | - | - |
| `/setup` | Administrador | Registra los canales de anuncios, registros, ranked y staff que usan las alertas y publica el anuncio de lanzamiento del bot en el canal de anuncios, mencionando a todos. Solo rellena las categorías que aún no tienen canal. La respuesta es privada. |
> [!WARNING]
>
> Los canales de `/setup` (en `bot/main.py`) y el de `/anuncio` (en `bot/commands/comunidad.py`) están fijados en el código para el servidor de VANTS. En otro servidor, cámbialos antes de usar esos comandos.
### Agente
`/agente` controla el agente de VANTS (web, launcher, base de datos y CI). Todas sus respuestas son privadas.
| Subcomando | Qué hace |
| - | - |
| `/agente estado` | Último ciclo del agente e incidentes abiertos. |
| `/agente informe` | Diagnóstico completo del agente. |
| `/agente incidentes` | Lista hasta 15 incidentes abiertos. |
| `/agente resolver id_incidente` | Marca un incidente como resuelto. |
| `/agente comando texto` | Envía una orden en texto libre al agente. |
Si el servicio HTTP del agente está configurado, `/agente comando` recibe la respuesta al momento. Si no, la orden queda en cola, el agente la procesa en su próximo ciclo (hasta unos 30 minutos) y la respuesta llega por mensaje directo.
> [!WARNING]
>
> `/agente` actúa sobre la plataforma y está pensado para el propietario. Restringe su uso a personas de confianza en **Ajustes del servidor → Integraciones**.
## Alertas automáticas
El bot publica por sí solo en el canal que corresponde a cada categoría. Cada 30 segundos registra lo nuevo en una cola y envía hasta 10 alertas pendientes.
| Alerta | Canal | Cuándo se envía |
| - | - | - |
| Nuevo torneo | Anuncios | Se crea un torneo (solo se miran los creados en el último día). |
| Nuevo evento | Anuncios | Se crea un evento (solo se miran los creados en el último día). |
| Evento a punto de empezar | Anuncios | Falta una hora o menos para que empiece un evento. |
| Resultado ranked | Ranked | Termina una partida ranked (solo se miran las del último día). |
* Cada elemento genera una sola alerta, aunque el bot lo vea en varios ciclos.
* Si un envío falla, el bot lo reintenta hasta 3 veces. Después la alerta queda como fallida y se puede reintentar desde la pestaña Alertas del panel de administración.
* Cada tipo de alerta se puede apagar desde ese panel.
* Los mensajes salen sin menciones: `@everyone` y `@here` se neutralizan en los textos.
* En cada ciclo el bot guarda también su latido, que el panel usa para mostrar si está conectado.
Los canales de cada categoría se leen de la tabla `discord_channels`, que rellena `/setup`.
## Tablas que usa
| Tabla | Uso |
| - | - |
| `players`, `leaderboard` (vista), `seasons` | Perfiles, ranking y temporadas |
| `tournaments`, `tournament_entries`, `events` | Torneos, inscripciones y eventos |
| `ranked_matches` | Partidas ranked |
| `discord_channels`, `bot_alerts`, `bot_heartbeat`, `bot_settings` | Canales, cola de alertas, latido e interruptores |
| `agent_*` | Órdenes, incidentes y memoria del agente |
Además lee las cuentas de Riot, Steam y Discord vinculadas para `/perfil`, `/verificar` y `/vincular`. Las tablas del bot están descritas en [Bot y eventos web](/referencia/esquema-base-de-datos#bot-y-eventos-web).
## Permisos y buenas prácticas
* El staff se reconoce por el nombre del rol. Crea esos roles solo para personas de confianza y limita quién puede gestionar roles en el servidor.
* `/setup` solo lo ejecutan administradores. Los comandos de moderación y de comunidad requieren ser staff.
* Si no quieres que todos los miembros usen las estadísticas, restríngelas a un canal desde Integraciones.
* Concede al bot solo los permisos de la tabla y colócalo por encima de los roles que deba moderar.
* Mantén el token fuera del repositorio y regéneralo con **Reset Token** si se filtra.
## Más información
* [Vantsgames](/repos/vantsgames): código del bot y del resto de la plataforma.
* [Esquema de la base de datos](/referencia/esquema-base-de-datos): tablas del bot, de las alertas y del agente.
* [Arquitectura](/arquitectura): cómo encaja el bot con el resto de componentes.
* [Seguridad](/referencia/seguridad): secretos, webhooks y buenas prácticas.
# Esquema de la base de datos
Source: https://vantsgg.mintlify.app/referencia/esquema-base-de-datos
Tablas, columnas clave, relaciones y estados de la base de datos de VANTS en Neon por área: torneos, matchmaking, ranked, bus de eventos, agente y bot.
La base de datos de VANTS es un Postgres en Neon. Su esquema se versiona como migraciones SQL en [VantsBasededatos](/repos/vants-base-de-datos). Esta página resume qué guarda cada área y cómo se relacionan las tablas principales.
> [!NOTE]
>
> Datos al 23 de octubre de 2026: 69 tablas en producción y 34 migraciones. Las migraciones de este repositorio crean 39 de esas tablas. Las otras 30, como `players`, `seasons` y `season_player_stats`, no las crea ninguna migración: las migraciones solo las referencian o las modifican. Las migraciones son la fuente de verdad: si algo no coincide con esta página, manda el SQL.
## Mapa por áreas
| Área | Tablas | Qué guarda |
| - | - | - |
| [Plataforma de torneos](#plataforma-de-torneos) | `pt_games`, `pt_torneos`, `pt_teams`, `pt_matches`, `pt_players`, `pt_articulos` | Catálogo público de juegos, torneos, equipos, partidas, jugadores y artículos |
| [Torneos de VANTS y presencia](#torneos-de-vants-y-presencia) | `tournament_teams`, `tournament_matches`, `presence` | Inscripciones, cuadro de eliminatorias y quién está conectado |
| [Matchmaking](#matchmaking) | `mm_tickets`, `mm_matches`, `mm_match_players`, `mm_invites`, `mm_ready` | Cola, partidas, participantes, invitaciones y confirmaciones |
| [Ranked y FACEIT](#ranked-y-faceit) | `series_ranked`, `faceit_matches`, `faceit_webhook_events`, `rr_historial` | Series, partidas de FACEIT, XP e historial de RR |
| [Bus de eventos](#bus-de-eventos) | `vants_events`, `vants_consumers`, `vants_bus_dedup`, `vants_feed` | Eventos que se intercambian los proyectos |
| [Agente](#agente) | `agent_runs`, `agent_events`, `agent_comandos`, `agent_memoria`, `agent_archivos`, `agent_sandbox_execs` | Vigilancia automática de la plataforma |
| [Bot y eventos web](#bot-y-eventos-web) | `bot_heartbeat`, `bot_settings`, `discord_channels`, `bot_alerts`, `vant_sync_events`, `web_events` | Estado del bot, alertas y eventos de la web |
| Jugadores y temporadas | `players`, `seasons`, `season_player_stats`, `profiles` | Perfiles, temporadas y estadísticas por temporada (rating, MMR, rango, victorias y derrotas) |
| Cuentas y pagos | `vants_links`, `purchases`, `entitlements`, `audit_logs` | Cuentas externas vinculadas, compras, accesos y auditoría |
## Convenciones
* Las claves primarias son `uuid` generados con `gen_random_uuid()`, salvo claves naturales como `slug`, `ticket` o `event_id`.
* Las fechas son `timestamptz`.
* Los estados son `text` con una restricción `CHECK` que lista los valores permitidos.
* Los datos de demostración llevan `is_demo = true` y se pueden ocultar con ese filtro.
* Los nombres de columnas mezclan español e inglés, según la migración que los creó.
## Plataforma de torneos
Catálogo público con prefijo `pt_` (migraciones 28 y 29). Lo lee la [API pública](/referencia/api-publica) y es independiente de los torneos propios de VANTS. Cada torneo, partida y jugador indica su origen en `source`.
```mermaid theme={null}
erDiagram
pt_games ||--o{ pt_torneos : "game_slug"
pt_games ||--o{ pt_matches : "game_slug"
pt_games |o--o{ pt_teams : "game_slug"
pt_games |o--o{ pt_players : "game_slug"
pt_games |o--o{ pt_articulos : "game_slug"
pt_torneos |o--o{ pt_matches : "tournament_id"
pt_teams |o..o{ pt_players : "team_slug"
pt_games {
text slug PK
text nombre
text corto
boolean activo
int orden
}
pt_torneos {
uuid id PK
text slug UK
text game_slug FK
text nombre
text estado
text region
numeric prize_value
timestamptz starts_at
timestamptz ends_at
}
pt_teams {
uuid id PK
text slug UK
text nombre
text tag
text game_slug FK
}
pt_matches {
uuid id PK
text game_slug FK
uuid tournament_id FK
text equipo_a
text equipo_b
int score_a
int score_b
text estado
timestamptz empieza_at
}
pt_players {
uuid id PK
text slug UK
text nombre
text game_slug FK
text team_slug
text rol
}
pt_articulos {
uuid id PK
text slug UK
text titulo
text game_slug FK
text estado
timestamptz publicado_at
}
```
El diagrama muestra solo las columnas principales. Tres detalles que conviene conocer:
* `pt_matches.equipo_a` y `equipo_b` guardan el nombre del equipo como texto, no una clave foránea.
* `pt_players.team_slug` apunta a `pt_teams.slug` sin clave foránea, así que borrar un equipo no toca a sus jugadores.
* Torneos, partidas y jugadores son únicos por (`source`, `external_id`), lo que permite importar datos externos sin duplicarlos.
Juegos registrados en `pt_games`: `valorant`, `cs2` y `dota2` están activos; `lol`, `r6s` y `cod` existen pero están inactivos. No se puede borrar un juego que tenga torneos o partidas.
| Campo | Valores permitidos |
| - | - |
| `source` (torneos, partidas y jugadores) | `vants`, `faceit`, `demo` |
| `pt_torneos.estado` | `upcoming`, `ongoing`, `finished`, `cancelled` |
| `pt_torneos.location_type` | `online`, `lan` |
| `pt_matches.estado` | `upcoming`, `live`, `finished`, `cancelled` |
| `pt_articulos.estado` | `draft`, `published` |
## Torneos de VANTS y presencia
Los torneos propios de VANTS usan sus propias tablas (migración 17). `tournament_id` es un identificador de texto, no una clave foránea a `pt_torneos`.
**`tournament_teams`** guarda las inscripciones.
| Columna | Detalle |
| - | - |
| `name` | De 2 a 32 caracteres. No se admiten dos equipos con el mismo nombre en un torneo, sin distinguir mayúsculas |
| `tag` | De 2 a 4 caracteres |
| `game` | Por defecto `valorant` |
| `captain`, `region` | Capitán y región del equipo |
| `status` | `pendiente`, `confirmado` o `rechazado` |
**`tournament_matches`** guarda el cuadro. Cada fila es única por (`tournament_id`, `ronda`, `slot`).
| Columna | Detalle |
| - | - |
| `ronda` | `q`, `s`, `f`, `l1`, `l2` o `lf` |
| `slot` | De 0 a 3 |
| `team_a`, `team_b` | Nombres de los equipos |
| `score_a`, `score_b` | Marcador |
| `estado` | `pendiente`, `en_curso` o `final` |
| `ganador` | Equipo ganador. El cuadro avanza cuando la partida está en `final` y tiene ganador |
### Cuadro de 8 equipos
El cuadro es de doble eliminación. Cuando un torneo llega a 8 equipos confirmados, un trigger lo siembra una sola vez: los 8 primeros por orden de inscripción forman los 4 cruces de cuartos y se crean vacías el resto de partidas. Con menos de 8 equipos confirmados no hay cuadro.
Al marcar una partida como `final` con su `ganador`, otro trigger mueve a los equipos:
| Ronda | Slots | El ganador pasa a | El perdedor pasa a |
| - | - | - | - |
| `q` (cuartos) | 0 a 3 | `s` | `l1` |
| `s` (semifinales) | 0 y 1 | `f` | `l2` |
| `l1` (inferiores, ronda 1) | 0 y 1 | `l2` | Queda eliminado |
| `l2` (inferiores, ronda 2) | 0 y 1 | `lf` | Queda eliminado |
| `lf` (final inferior) | 0 | `f`, si ese hueco sigue libre | Queda eliminado |
La gran final (`f`, slot 0) cierra el cuadro. Si un equipo no avanza solo, el propietario puede colocarlo a mano.
### Presencia
`presence` tiene una fila por jugador conectado.
| Columna | Detalle |
| - | - |
| `player_id` | Clave primaria, referencia a `players` |
| `estado` | `online`, `jugando` o `ausente` |
| `juego` | Juego actual |
| `actualizado` | Se renueva en cada actualización |
La web y la app de escritorio envían un latido cada 60 segundos.
### Vistas
Para mostrar datos públicos usa estas vistas en lugar de las tablas.
| Vista | Qué devuelve |
| - | - |
| `v_en_vivo` | Jugadores con presencia en los últimos 120 segundos y estado distinto de `ausente`, con sus datos básicos y su MMR más reciente |
| `v_torneo_equipos` | Equipos inscritos en un torneo, sin los rechazados y sin datos de quien inscribe |
| `v_torneo_bracket` | Todas las partidas del cuadro |
## Matchmaking
El matchmaking persiste la cola en Postgres (migraciones 21 a 24, 30 a 32) para que sobreviva a los reinicios y el emparejamiento sea transaccional. Cada fila guarda además la identidad del jugador; esas columnas no se detallan aquí.
```mermaid theme={null}
erDiagram
mm_matches ||--o{ mm_match_players : "match_id"
mm_matches ||--o{ mm_ready : "match_id"
mm_matches |o..o{ mm_tickets : "match_id"
mm_matches {
uuid id PK
text game
text mode
text region
text status
int score_a
int score_b
timestamptz ready_deadline
}
mm_match_players {
uuid match_id PK, FK
smallint team
int rr_before
int rr_after
}
mm_ready {
uuid match_id PK, FK
timestamptz listo_at
}
mm_tickets {
text ticket PK
text game
text mode
text region
text status
uuid match_id
timestamptz expires_at
}
mm_invites {
uuid id PK
text game
text mode
text region
text status
timestamptz expires_at
}
```
`mm_tickets.match_id` no es una clave foránea: es la referencia a la partida a la que se asignó el ticket.
### Modos
| Modo | Jugadores por partida |
| - | - |
| `duo` | 2 (1v1) |
| `team` | 10 (5v5) |
Cada ticket indica `game`, `mode` y `region` (por defecto `EU`). Un jugador solo puede tener un ticket activo por juego. El emparejador toma los primeros jugadores de la cola por orden de llegada (FIFO) y crea la partida solo si hay exactamente los necesarios, sin bloquear a otros emparejadores en paralelo.
### Estados
Ticket:
```mermaid theme={null}
flowchart LR
queued --> matched
queued --> cancelled
queued --> expired
```
Partida:
```mermaid theme={null}
flowchart LR
propuesto --> reportado --> verificado
propuesto --> anulado
```
* `propuesto`: el emparejador creó la partida y fijó `ready_deadline` a 120 segundos.
* `reportado` y `verificado`: un participante reporta el marcador, se aplica el RR a cada jugador y se guardan `rr_before` y `rr_after`. Aplicar el resultado es idempotente.
* `anulado`: venció el plazo sin que todos confirmaran. La anulación es perezosa: la dispara el primer participante que consulta la partida después del plazo. Los tickets quedan como historial y los jugadores pueden volver a ponerse en cola.
### Invitaciones
`mm_invites` guarda invitaciones entre jugadores para jugar `duo` o `team`. Caducan a los 10 minutos y pasan de `pendiente` a `aceptada`, `rechazada`, `cancelada` o `expirada`. Solo puede haber una pendiente por pareja de jugadores y juego.
### Funciones
El cliente web y la app de escritorio usan las mismas funciones SQL, de modo que hay una sola fuente de verdad.
| Función | Qué hace |
| - | - |
| `mm_encolar` | Pone al jugador en cola y devuelve su ticket, reutilizando uno vivo si existe |
| `mm_ticket_estado` | Devuelve la posición, el estado y la partida del ticket. En cola añade el total de tickets vivos y cuántos jugadores hacen falta |
| `mm_salir` | Saca al jugador de la cola |
| `mm_listo` | Confirma la participación en una partida propuesta |
| `mm_estado_ready` | Devuelve cuántos participantes están listos, el plazo y si ya venció |
| `mm_anular_si_timeout` | Anula la partida si venció el plazo sin todas las confirmaciones |
| `mm_reportar` | Reporta el marcador y aplica el RR |
| `mm_historial_de` | Devuelve el historial de RR de un jugador |
| `mm_invitar` | Envía una invitación a otro jugador, al que se localiza por su nick |
| `mm_invites_pendientes` | Lista las invitaciones pendientes |
| `mm_responder_invite` | Acepta o rechaza una invitación |
La vista `mm_historial` reúne, por jugador, las partidas en las que participó, con su equipo, el marcador, el RR antes y después y el resultado.
## Ranked y FACEIT
### Escala de rango
El rango se calcula a partir del rating del jugador. El RR que se muestra es el rating multiplicado por 100.
| Rating mínimo | Rango |
| - | - |
| Menos de 0.95 | FUNDICIÓN (solo en la web de vants-dev-launcher) |
| 0.95 | ACERO |
| 1.00 | TITANIO |
| 1.05 | OBSIDIANA |
| 1.11 | CROMO |
| 1.17 | PLASMA |
| 1.24 | ÉTER |
| 1.30 | VANTS |
La web de vants-dev-launcher muestra el rango VANTS con el nombre APEX. La web de Vantsgames no muestra estos nombres: en `/ranking#rangos` presenta VANTS RANKED, una escalera de 9 rangos por XP que va de CENIZA (0 XP) a SUPREMO (8000 XP) en pasos de 1000 XP. Está definida en `vants-server/public/ranking.html` y `vants-server/public/static/js/perfil_pagina.js`.
Sin rating no hay rango y el jugador está en partidas de colocación, que parten de 0.95. Las partidas de FACEIT y las internas aplican la misma regla (migraciones 10 y 22):
| Resultado | Cambio de rating |
| - | - |
| Victoria | +0.015 |
| Empate | +0.002 |
| Derrota | -0.012 |
El rating siempre queda entre 0.50 y 1.60.
### Series
`series_ranked` guarda series entre un retador y un rival. Las reporta un jugador y las confirma el rival o el propietario (`pendiente`, `confirmada` o `rechazada`). Al confirmarse, se aplica el Elo a `players.rating` y se actualizan victorias, derrotas, MMR y rango en `season_player_stats`. La tabla guarda el rating de ambos antes y después. Las funciones son `reportar_serie`, `confirmar_serie`, `rechazar_serie` y `mis_series`.
### FACEIT
* `faceit_matches`: una fila por partida de FACEIT ya procesada. `faceit_match_id` es único, así que una partida nunca da XP dos veces. Guarda `result` (`win`, `loss` o `draw`), `xp_awarded` y `elo_delta`.
* `faceit_webhook_events`: eventos crudos que llegan por webhook, con su `payload` y la marca `processed`. Las funciones `faceit_procesar_eventos` y `faceit_aplicar_resultado` los convierten en partidas.
La experiencia sube +100 XP por victoria y +25 por empate; la derrota no suma. El nivel es `1 + floor(xp / 500)`. El procesador soporta `cs2` (`csgo` se normaliza a `cs2`) y `valorant`.
### Historial de RR
`rr_historial` registra cada cambio de rating, venga de donde venga.
| Columna | Detalle |
| - | - |
| `motivo` | `faceit_partida`, `match_interno` o `ajuste_manual` |
| `fuente` | `faceit`, `interno` o `manual` |
| `partida_id` | Identificador de la partida de origen |
| `temporada` | Temporada activa en ese momento |
| `game`, `mode`, `region` | Contexto de la partida |
| `rr_before`, `rr_after` | RR antes y después |
| `rr_delta` | Columna generada: `rr_after - rr_before` |
Es única por (`player_id`, `fuente`, `partida_id`), así que repetir una llamada no duplica líneas. Las vistas `rr_historial_jugador` y `rr_resumen` leen de ella, y `rr_temporada_actual` devuelve la temporada vigente.
## Bus de eventos
Los proyectos de VANTS se comunican con eventos inmutables (migraciones 33 y 34). El diagrama sigue un resultado de FACEIT desde `faceit_webhook_events` (migración 09) hasta quienes leen el bus. En mayúsculas aparece lo que no es una tabla: FACEIT y los cursores de flask-agents y de VANTS HUB, que cada uno guarda en un archivo local.
```mermaid theme={null}
erDiagram
FACEIT ||--o{ faceit_webhook_events : "envía webhooks"
faceit_webhook_events ||--o{ vants_bus_dedup : "evita duplicados por event_id"
faceit_webhook_events ||--o{ vants_events : "el puente publica match.*"
vants_events ||--o{ vants_feed : "vants-consumer-playvants copia"
vants_events ||--o{ CURSOR_FLASK_AGENTS : "flask-agents lee con su cursor"
vants_events ||--o{ CURSOR_VANTS_HUB : "VANTS HUB lee con su cursor"
faceit_webhook_events {
bigserial id PK
text event_type
jsonb payload
timestamptz created_at
boolean processed
}
vants_bus_dedup {
text event_id PK
timestamptz seen_at
}
vants_events {
text id PK "evt_ + ULID"
text type "match.finished, post.generated..."
text source "faceit-webhook, flask-agents..."
timestamptz ts
jsonb data
timestamptz created_at
}
vants_feed {
text id PK "mismo id del evento del bus"
text type
text source
timestamptz ts
jsonb data
timestamptz consumed_at "se conserva 7 días"
}
CURSOR_FLASK_AGENTS {
json cursor "archivo local, ruta en VANTS_BUS_CURSOR"
}
CURSOR_VANTS_HUB {
json cursor "archivo local en userData de Electron"
}
vants_consumers {
text project_id PK
text cursor
timestamptz updated_at
}
```
| Tabla | Para qué sirve |
| - | - |
| `vants_events` | Registro de eventos: `id` (`evt_` más un ULID), `type`, `source`, `ts` y `data` en `jsonb`. Se conservan 30 días |
| `vants_consumers` | Posición de lectura (`cursor`) de cada proyecto consumidor. Ningún cliente la usa todavía: flask-agents y VANTS HUB guardan su cursor en un archivo local, y `vants-consumer-playvants` lo calcula a partir de `vants_feed` |
| `vants_bus_dedup` | Eventos ya vistos, para no procesar dos veces el mismo (por ejemplo, el webhook de FACEIT) |
| `vants_feed` | Eventos ya ordenados y listos para mostrar en la web (resultados, torneos, clips, alertas), con la marca `consumed_at`. Se conservan 7 días. La llena `vants-consumer-playvants`; la web todavía no la lee |
Tipos de evento de ejemplo: `match.finished`, `clip.published` y `alert.detected`.
## Agente
El agente vigila la plataforma y deja su trabajo en estas tablas (migraciones 25 y 26).
| Tabla | Qué guarda |
| - | - |
| `agent_runs` | Cada ejecución: fuente (`ci`, `api`, `cli` o `discord`), modo (`ciclo`, `informe` o `comando`), estado global (`UP`, `DEGRADED`, `DOWN` o `DESCONOCIDO`), hallazgos, acciones y resumen |
| `agent_events` | Incidencias detectadas, con `dedupe_key` única, severidad (`info`, `warn`, `error` o `critical`) y estado (`abierto`, `gestionando`, `resuelto` o `archivado`) |
| `agent_comandos` | Órdenes enviadas con `/agente comando`, su estado (`pendiente`, `hecho` o `error`) y la respuesta que entrega el bot por mensaje directo |
| `agent_memoria` | Memoria del agente por clave: tipo (`hecho`, `aprendizaje`, `preferencia` o `nota`), importancia de 1 a 5 y caducidad opcional |
| `agent_archivos` | Archivos que genera el agente, únicos por ruta, con su tamaño y huella |
| `agent_sandbox_execs` | Comandos ejecutados en un entorno aislado, con su código de salida y duración |
La vista `agent_resumen` cuenta las incidencias abiertas, los comandos pendientes y el estado de la última ejecución. La vista `agent_memoria_resumen` cuenta los recuerdos, los archivos y las ejecuciones.
## Bot y eventos web
Tablas de la migración 02 y de la migración 01.
| Tabla | Qué guarda |
| - | - |
| `bot_heartbeat` | Último latido del bot: `last_seen`, `latency_ms`, número de servidores y versión |
| `bot_settings` | Interruptores del bot por clave (`key`, `enabled`) |
| `discord_channels` | Canal de Discord que corresponde a cada categoría de aviso |
| `bot_alerts` | Alertas pendientes de enviar, únicas por (`kind`, `ref_id`), con su estado, intentos y error |
| `vant_sync_events` | Eventos de la web que se sincronizan con Discord, con el estado de cada intento de entrega |
| `web_events` | Eventos que la web envía al Supervisor Bot: tipo, severidad, página y estado de entrega a Discord |
El detalle de los comandos del bot está en [Bot de Discord](/referencia/bot-de-discord).
## Otras tablas
Hay además tablas en producción que ninguna migración crea. Por su nombre, parecen pertenecer a estas áreas. Revisa la base de datos antes de depender de ellas:
* Soporte: `support_tickets`, `support_ticket_messages`, `tickets`.
* Eventos y comunidad: `events`, `event_rsvps`, `postulaciones`.
* Pase y planes: `pass_progreso`, `pass_recompensas`, `plan_content`.
* Ranked: `ranked_rules`, `ranked_matches`, `ranked_history`.
* Torneos e inscripciones: `tournaments`, `tournament_entries`.
Las tablas de administración y de cuentas vinculadas son internas y no se documentan aquí.
## Cómo añadir una migración
Crea un archivo nuevo en `db/migrations/` con el siguiente número y escríbelo de forma idempotente. El flujo completo, con pruebas en una rama de Neon, está en [VantsBasededatos](/repos/vants-base-de-datos).
## Más información
* [VantsBasededatos](/repos/vants-base-de-datos): estructura del repositorio y flujo de trabajo
* [API pública](/referencia/api-publica): rutas que leen estas tablas
* [Arquitectura](/arquitectura): cómo encaja la base de datos con el resto de VANTS
* [vants-faceit-webhook](/repos/vants-faceit-webhook): origen de los eventos de FACEIT
# Seguridad
Source: https://vantsgg.mintlify.app/referencia/seguridad
Cómo protege VANTS la identidad, los datos, la web, los webhooks y los secretos, cómo rotar un secreto y qué revisar antes de fusionar un cambio a main.
Esta página resume las medidas de seguridad de la plataforma VANTS a nivel de concepto. El código de cada repositorio es la fuente de verdad: si algo no coincide con él, manda el código.
> [!NOTE]
>
> Si encuentras una vulnerabilidad, no la publiques en un issue ni en un canal abierto. Avisa en privado al equipo de VANTS.
## Capas de defensa
| Capa | Qué protege | Cómo |
| - | - | - |
| Identidad | Las cuentas de usuario | Auth0 gestiona el inicio de sesión y las contraseñas. Las rutas protegidas validan el token de acceso. |
| Borde | La web y la API | El Worker de Cloudflare filtra bots, limita el uso por IP, comprueba el origen y añade cabeceras de seguridad. |
| Datos | La base de datos en Neon | La lectura pública solo ve tablas permitidas y sin columnas de identidad. Las escrituras de usuario usan su propio token. |
| Webhooks | Las entradas de Stripe, Discord, FACEIT y Vercel | Cada petición se verifica con la firma o el secreto de su proveedor. |
| Secretos y CI | Credenciales y despliegues | Los secretos viven fuera del código y los workflows usan permisos mínimos. |
| Bot de Discord | El servidor de la comunidad | Los comandos de moderación, comunidad y configuración exigen ser staff o administrador. |
## Identidad y sesiones
El inicio de sesión lo gestiona Auth0, así que VANTS no guarda contraseñas. Las rutas que actúan en nombre de un usuario piden la cabecera `Authorization: Bearer ` y el servidor valida el token antes de responder. Las lecturas públicas, como torneos, equipos o artículos, no necesitan sesión.
Trata los tokens como credenciales: no los pegues en issues, registros ni capturas de pantalla.
## Datos
La base de datos vive en Neon y tiene dos caminos de acceso:
* **Lectura pública.** Una función de solo lectura expone únicamente las tablas de una lista permitida. Quita de las respuestas las columnas de identidad, como el correo, parametriza los valores, limita el número de filas y el uso por IP, y no devuelve los errores internos de la base de datos. Una tabla nueva no se puede leer por este camino hasta que se añade a la lista de forma explícita.
* **Escrituras de usuario.** Las que se hacen desde la web usan el token de Auth0 del propio usuario.
La base de datos aplica reglas de acceso por fila (RLS) para limitar qué filas puede leer o modificar cada usuario. Cada tabla define las suyas, así que revisa las de una tabla antes de asumir que sus filas son privadas. Al crear una tabla nueva, decide quién puede leerla y escribirla antes de desplegar su migración.
Consulta el [esquema de la base de datos](/referencia/esquema-base-de-datos) para ver qué tablas existen.
## La web y la API
El Worker de Cloudflare atiende todas las peticiones antes de servir la web o la API.
### Cabeceras de seguridad
El Worker añade estas cabeceras a las páginas y a las respuestas de la API:
| Cabecera | Efecto |
| - | - |
| `Strict-Transport-Security` | Obliga al navegador a usar HTTPS durante un año, también en los subdominios. |
| `Content-Security-Policy` | Limita desde qué orígenes se cargan scripts, estilos, fuentes y marcos, y prohíbe que otros sitios incrusten la web. |
| `X-Frame-Options` | Refuerza esa prohibición de incrustar la web en un marco. |
| `X-Content-Type-Options` | Impide que el navegador adivine el tipo de un archivo. |
| `Referrer-Policy` | Al salir a otro sitio, solo comparte el origen y no la ruta completa. |
| `Permissions-Policy` | Desactiva funciones del navegador que la web no usa, como cámara, micrófono, geolocalización, pagos y USB. |
### Origen, bots y límites
* **Origen.** Las rutas de la API que usa la web comprueban la cabecera `Origin`. Una petición de un navegador que llega desde un dominio ajeno a VANTS recibe `403`, y las solicitudes previas (preflight) solo se aprueban para dominios de VANTS. Es una protección para navegadores, no una autenticación: las peticiones sin `Origin` (curl, scripts, servidores) se aceptan, por eso las rutas con datos de usuario exigen además un token.
* **Bots y escáneres.** Se rechazan las peticiones sin `User-Agent` o con uno bloqueado, y las rutas típicas de escáneres. Los rastreadores legítimos de buscadores siguen pasando.
* **Límite de uso.** Hay un límite de peticiones por IP. Al superarlo recibes `429` con `Retry-After`. Cada instancia del Worker lleva su propia cuenta, así que es una defensa contra inundaciones y no una cuota exacta.
* **Métodos y tamaño.** El Worker solo acepta los métodos que usa la plataforma (`405` para el resto) y limita el tamaño del cuerpo de cada ruta.
Los detalles de cada ruta están en [API pública](/referencia/api-publica#reglas-generales).
## Webhooks
Un webhook es una entrada pública por naturaleza. Por eso cada evento se verifica antes de procesarlo, y uno con firma o secreto inválido se rechaza.
| Webhook | Qué se comprueba |
| - | - |
| Stripe | La firma del evento. Si falta el secreto, la petición se rechaza en lugar de aceptarse sin verificar. |
| Discord (interacciones) | La firma de cada petición. Las inválidas reciben `401`. |
| FACEIT | El secreto compartido. Consulta [vants-faceit-webhook](/repos/vants-faceit-webhook). |
| Vercel | La firma del cuerpo. El endpoint además tiene un límite de peticiones por IP. |
## Secretos
Los secretos no viven en el código. Cada uno se guarda en el sistema del servicio que lo usa:
| Dónde | Qué guarda |
| - | - |
| Secretos del Worker de Cloudflare | Credenciales de la API y de los webhooks. |
| Secretos del repositorio en GitHub | Credenciales de los workflows: despliegue, migraciones y sincronizaciones. El despliegue del Worker copia algunos a Cloudflare. |
| Configuración de las funciones de Neon | Credenciales de esas funciones. |
| Variables de entorno del bot | El token del bot y la cadena de conexión a la base de datos. |
Los archivos `.env.example` son plantillas y no deben contener valores reales. Cópialos a `.env` en tu equipo y no subas nunca el `.env`: el `.gitignore` de Vantsgames ya lo excluye, igual que las claves `.pem` y `.key`.
> [!WARNING]
>
> Git conserva el historial: borrar un secreto en un commit posterior no lo elimina de los anteriores. Si una credencial llega a un commit, a un issue o a un registro, trátala como comprometida y rótala. Importa más en los repositorios públicos, donde cualquiera puede leer el historial.
### Rotar un secreto
**1. Genera una credencial nueva**
Hazlo en el servicio de origen: Cloudflare, Neon, Discord, FACEIT, Stripe u otro.
**2. Actualízala donde se usa**
Para el Worker, ejecuta `npx wrangler secret put `. Para los workflows, cambia el secreto en **Settings → Secrets and variables → Actions** del repositorio. Para el bot, cambia su variable de entorno. Si el mismo secreto existe en varios sitios, cámbialo en todos: el despliegue del Worker copia a Cloudflare algunos secretos del repositorio y sobrescribiría el valor nuevo con uno antiguo.
**3. Aplica el valor nuevo**
Reinicia o vuelve a desplegar el servicio si hace falta para que tome el valor nuevo.
**4. Revoca la credencial anterior**
Hazlo en el servicio de origen y revisa sus registros por si alguien la usó.
## Bot de Discord
Los comandos se agrupan en tres niveles: todos los miembros, staff y administrador. [Bot de Discord](/referencia/bot-de-discord) los lista comando por comando.
* El staff se reconoce por el nombre del rol. Crea esos roles solo para personas de confianza y limita quién puede gestionar roles en el servidor.
* `/setup` solo lo ejecutan administradores.
* `/agente` actúa sobre la plataforma. Restringe su uso a personas de confianza en **Ajustes del servidor → Integraciones**.
* Concede al bot solo los permisos que necesita.
* Guarda el token del bot fuera del código y rótalo si se filtra.
## Despliegues y CI
Los workflows viven en `.github/workflows/` de Vantsgames. Estos son los que tocan producción:
| Workflow | Cuándo se ejecuta | Qué hace |
| - | - | - |
| `tests` | En push y pull requests a `main` que cambian `scripts/`, `vants-server/`, `neon-functions/` o `db/` | Ejecuta las pruebas automáticas. |
| `cloudflare-deploy` | Al llegar a `main` cambios en `vants-server/`, o a mano | Despliega el Worker. |
| `neon-deploy-functions` | Al llegar a `main` cambios en `neon-functions/`, o a mano | Despliega las funciones de Neon. |
| `neon-migrate` | Al llegar a `main` cambios en `db/migrations/`, o a mano | Aplica las migraciones SQL a la base de datos real. |
Otros workflows programados vigilan el servicio, ejecutan el agente y sincronizan datos de FACEIT.
* Todos los workflows declaran permisos mínimos: son de solo lectura, salvo el de migraciones, que necesita escribir en el repositorio.
* Los despliegues y las migraciones comparten una cola: se ejecutan de uno en uno y no se cancelan a medias.
> [!NOTE]
>
> Antes de fusionar a `main`, comprueba que `tests` esté en verde: el despliegue del Worker es un workflow aparte y no espera a las pruebas.
> [!WARNING]
>
> Las migraciones nuevas que llegan a `main` se aplican solas a la base de datos real, sin aprobación manual. Revísalas y pruébalas antes de fusionar, y escríbelas para que se puedan ejecutar más de una vez.
Para revertir un despliegue, usa `git revert ` y haz push a `main`: el CI vuelve a desplegar. Revertir el archivo de una migración no deshace lo que ya aplicó en la base de datos: escribe una migración nueva que lo corrija.
## Buenas prácticas para contribuir
* No subas secretos, tokens, archivos `.env`, claves ni identificadores privados. Usa las plantillas `.env.example`.
* No pegues tokens de acceso, cadenas de conexión ni capturas con credenciales en issues, pull requests o chats.
* En una ruta nueva que modifique datos, valida el token y comprueba que el usuario puede cambiar ese recurso.
* Valida las entradas y limita su tamaño. No devuelvas al cliente errores internos ni detalles del servicio de origen.
* Si una tabla nueva contiene datos personales, no la hagas legible por la API pública.
* En los workflows, declara permisos mínimos y fija las acciones de terceros por hash de commit, no por etiqueta.
* Mantén las dependencias al día.
## Más información
* [Arquitectura](/arquitectura): componentes, flujo de una petición y CI/CD.
* [API pública](/referencia/api-publica): reglas generales, errores y rutas.
* [Esquema de la base de datos](/referencia/esquema-base-de-datos): tablas y áreas del esquema.
* [Bot de Discord](/referencia/bot-de-discord): comandos y permisos.
* [Vantsgames](/repos/vantsgames): repositorio principal, despliegue y reversión.
* [vants-faceit-webhook](/repos/vants-faceit-webhook): validación del secreto de FACEIT.
# flask-agents
Source: https://vantsgg.mintlify.app/repos/flask-agents
Web VANTS AI, cuartel general de los agentes de VANTS: el squad, el chat Vants Soporte con GLM y el consumidor de VANTS-BUS, en Flask sobre Vercel.
flask-agents es VANTS AI ([vants-ai.vercel.app](https://vants-ai.vercel.app)), la web del cuartel general de los agentes de VANTS. Presenta el squad, sirve el chat de soporte Vants Soporte y contiene el consumidor de [VANTS-BUS](/conexiones-entre-repositorios#vants-bus), el agente más antiguo del squad. Es una app Flask para Python 3.9 o superior, con Flask 3.1 y gunicorn 23, que se despliega en Vercel.
## El squad
| Agente | Generación | Dónde vive su código | Qué hace |
| - | - | - | - |
| Agente VANTS | Nuevo | `services/agent-service/` y workflow `agente.yml` de [Vantsgames](/repos/vantsgames) | Revisa la plataforma cada 30 minutos, decide con reglas y GLM, relanza lo que falla y guarda su memoria en Neon |
| VANTCALL (ORION) | Nuevo | `bot/` de Vantsgames | El mando del dueño en Discord: comandos `/agente` y avisos por mensaje directo. En la web aparece como ORION |
| Vants Soporte | Nuevo | Este repositorio: `/soporte` y `POST /api/chat` | Chat de soporte en español con GLM y un modo reglas de respaldo |
| Consumidor de VANTS-BUS | Viejo | `vants_bus/` de este repositorio | Escucha nueve tipos de evento del bus y publica `post.generated` sobre partidos y torneos |
| Agentes de desarrollo | Nuevo | `CLAUDE.md`, `DECISIONS.md`, ramas y PR | Construyen el código. Nada llega a producción sin pasar por una persona |
Solo Vants Soporte y el consumidor del bus tienen código en este repositorio. La web presenta a los demás con los datos de `agentes/squad.py`, los mismos que devuelve `GET /api/agentes`.
## Cómo se conecta el cuartel
El squad no vive en un solo repositorio: los recuadros indican dónde está el código de cada pieza. En azul, los agentes; en verde, el resto de piezas de VANTS; en amarillo, los servicios externos.
```mermaid theme={null}
flowchart TB
subgraph VG [Vantsgames]
WF["agente.yml
cada 30 minutos"]
AG["Agente VANTS
services/agent-service"]
MD["Mando por Discord
VANTCALL (ORION) o vantsbot"]
WV["Worker vants
la web"]
end
subgraph FA ["flask-agents · VANTS AI en Vercel"]
WEB["Web VANTS AI
vants-ai.vercel.app"]
SOP["Vants Soporte
/soporte y /api/chat"]
CON["Consumidor de VANTS-BUS
vants_bus/"]
end
subgraph EXT [Servicios externos]
GH[GitHub Actions]
VC["Vercel
proyecto vants"]
GLM[GLM]
end
FW[vants-faceit-webhook]
HUB["VANTS HUB
vants-dev-launcher"]
DB[VantsBasededatos]
BUS[VANTS-BUS]
N[("Neon
tablas agent_*")]
WF -->|lanza un ciclo| AG
AG <-->|memoria y cola de órdenes| N
MD <-->|"órdenes, incidentes y respuestas"| N
DB -->|migraciones 25 y 26| N
AG -->|chequea| WV
AG -->|"chequea /healthz"| FW
AG -->|revisa sus releases| HUB
AG -->|relanza lo que falla| GH
AG -->|revisa y relanza deploys| VC
AG -->|function calling| GLM
SOP -->|"chat/completions"| GLM
WV -->|enlaza a VANTS AI| WEB
FW -->|eventos de partidos| BUS
WV -->|"inscripciones, clips y resultados"| BUS
BUS -->|9 tipos de evento| CON
CON -->|"post.generated"| BUS
BUS -->|"eventos, incluido post.generated"| HUB
classDef azul fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef verde fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef amarillo fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef gris fill:#f3f4f6,stroke:#9ca3af,color:#4b5563
class AG,MD,SOP,CON azul
class WF,WV,WEB,FW,HUB,DB verde
class GH,VC,GLM amarillo
class BUS,N gris
```
* **Agente VANTS.** `agente.yml` lo lanza cada 30 minutos, a mano o cuando Vercel avisa de un deploy fallido. Revisa la web, vants-faceit-webhook, las releases de VANTS HUB, los workflows de Vantsgames, Neon, el último deploy de Vercel y, si está configurado, el servicio de matchmaking. Relanza una sola vez cada workflow que falla y hace como mucho dos redeploys de Vercel por commit. Decide con reglas y, si tiene GLM configurado, con function calling.
* **Mando por Discord.** `/agente comando` deja la orden en `agent_comandos` y el agente la atiende en su siguiente ciclo. Los incidentes y las respuestas llegan al dueño por mensaje directo. Lo hacen el bot VANTCALL y la Neon Function `vantsbot`, que no necesita el bot encendido. Los comandos están en [Bot de Discord](/referencia/bot-de-discord#agente).
* **VANTS AI.** Este repositorio no usa Neon ni llama al Agente VANTS. Vants Soporte habla con GLM por `chat/completions`, sin function calling, y el consumidor solo habla con VANTS-BUS. La web de Vantsgames enlaza a VANTS AI desde su portada.
* **Memoria del agente.** Las tablas `agent_*` salen de las migraciones 25 y 26 de [VantsBasededatos](/repos/vants-base-de-datos). Sus columnas están en [Esquema de la base de datos](/referencia/esquema-base-de-datos#agente).
## Estructura
```text theme={null}
main.py App Flask: registra páginas y API y arranca el consumidor del bus
api/index.py Entrada del runtime Python de Vercel
endpoints/
web.py Páginas
api.py API JSON
agentes/squad.py Datos del squad que muestra la web
soporte/
ia.py Chat con GLM y caída al modo reglas
reglas.py Modo reglas: respuestas en español sin dependencias
templates/ Plantillas HTML de las páginas
static/ CSS, JavaScript e imágenes
vants_bus/
consumer.py Consumidor del bus y reacciones por tipo de evento
vants_bus_client.py Cliente del bus, solo con la biblioteca estándar
snippet_flask.py Ejemplo de integración con Flask
public/ Favicon
.env.example Variables de entorno, sin valores
requirements.txt Dependencias para Vercel: Flask y gunicorn
pyproject.toml Dependencias del proyecto
```
Las páginas y los estáticos se copiaron tal cual del despliegue en vivo de VANTS AI. El backend se reconstruyó para responder a las mismas rutas.
## Páginas
| Ruta | Página |
| - | - |
| `/` | Portada: el squad, cómo trabajan los agentes y con qué están hechos |
| `/agente/` | Ficha de un agente: `agente-vants`, `orion`, `soporte` o `dev-agents`. Con otro `id` responde 404 |
| `/soporte` | Chat de Vants Soporte |
| `/estado` | Panel de estado del squad |
| `/creador` | Espacio oficial del creador |
> [!NOTE]
>
> En `main`, `/estado` muestra siempre la vista local ("MODO LOCAL · SIN DATOS REALES"). `.env.example` incluye `AGENT_URL` y `AGENT_TOKEN` para consultar el estado real del Agente VANTS, pero el código de este repositorio todavía no las lee.
## API
| Método | Ruta | Respuesta |
| - | - | - |
| `GET` | `/healthz` | `{"ok": true, "servicio": "web-agentes-vants"}` |
| `GET` | `/api/agentes` | Los datos del squad de `agentes/squad.py` |
| `GET` | `/api/ia` | Estado del chat: `activo` (hay clave de GLM), `modelo`, `proveedor` y `fallback` |
| `POST` | `/api/chat` | Respuesta de Vants Soporte. Ver [Chat de soporte](#chat-de-soporte) |
| `GET` | `/vants/bus` | Estado del consumidor del bus |
`/vants/bus` indica en `activo` si el consumidor arrancó dentro de Flask y, en `consumidor`, los contadores `procesados`, `publicados` y `errores`, el último evento recibido (`ultimo_evento`) y el último error (`ultimo_error`). Solo refleja el consumidor que corre dentro de Flask, no el proceso independiente.
## Chat de soporte
`POST /api/chat` recibe el mensaje y, si quieres, el historial de la conversación:
```json theme={null}
{
"mensaje": "quiénes son los agentes",
"historial": [{ "rol": "user", "texto": "hola" }]
}
```
Y responde:
```json theme={null}
{ "ok": true, "modo": "llm", "modelo": "glm-4.5-flash", "respuesta": "..." }
```
* Con clave de GLM, responde el modelo (`"modo": "llm"`) como Vants Soporte y en español. Del historial usa los 10 últimos turnos.
* Sin clave, o si GLM falla, responde el modo reglas (`"modo": "reglas"`, `"modelo": null`). Busca palabras clave sin tener en cuenta los acentos y solo cuenta lo que ya dice la web. El chat nunca devuelve un error 500.
* Si falta `mensaje` o supera 2000 caracteres, responde 400 con `falta_mensaje` o `mensaje_demasiado_largo` en `error`.
## Variables de entorno
`.env.example` lista todas las variables. Son opcionales: sin ellas, la web arranca con esas funciones apagadas. La app las lee del entorno; en Vercel se definen en el proyecto.
| Variable | Para qué sirve | Si falta |
| - | - | - |
| `AGENT_LLM_KEY` | Clave de GLM para el chat | El chat responde en modo reglas |
| `AGENT_LLM_URL` | Proveedor compatible con la API de OpenAI | Usa `https://api.z.ai/api/paas/v4` |
| `AGENT_LLM_MODEL` | Modelo del chat | Usa `glm-4.5-flash` |
| `VANTS_BUS_URL` | URL de VANTS-BUS | El consumidor queda apagado |
| `VANTS_BUS_SECRET` | Secreto de `flask-agents` en el bus | El consumidor queda apagado |
| `VANTS_BUS_CURSOR` | Archivo donde el consumidor guarda su posición | Usa `vants_bus/.vants-cursor.json` |
| `AGENT_URL` y `AGENT_TOKEN` | Servicio HTTP del Agente VANTS | El código todavía no las usa |
## Consumidor del bus
`vants_bus/consumer.py` es el agente viejo del squad. Se identifica en el bus como `flask-agents` y escucha nueve tipos de evento: `match.finished`, `match.scheduled`, `match.canceled`, `tournament.inscriptions_opened`, `inscription.created`, `clip.published`, `clip.highlights_ready`, `game.session_started` y `alert.detected`.
### Reacciones
| Evento | Reacción |
| - | - |
| `match.finished` | Redacta `Resultado verificado: - ` y lo publica como `post.generated` con `kind: "match_result"` y el `matchId` |
| `tournament.inscriptions_opened` | Publica `post.generated` con `kind: "tournament_open"` y el texto `Se abrieron las inscripciones: ` |
| Resto de tipos | Solo los registra en el log |
Los `post.generated` llevan `channel: "discord"` como destino previsto. Hoy los recibe [VANTS HUB](/repos/vants-dev-launcher), que los muestra como notificación "Nueva publicación".
Para cambiar las reacciones, edita `reaccionar` en `consumer.py` o pasa tu propia función a `iniciar(handler=...)`. La función recibe el evento y el cliente del bus. Con `tipos=[...]` cambias los tipos que escucha.
Si una reacción falla, el error se cuenta y el consumidor sigue con el siguiente evento. Si el bus no responde, registra el error y vuelve a intentarlo.
### Cómo arrancarlo
El consumidor necesita `VANTS_BUS_URL` y `VANTS_BUS_SECRET` (el secreto de `flask-agents`). Sin ellas queda apagado y la app funciona igual.
| Modo | Cómo arranca | Cuándo usarlo |
| - | - | - |
| Proceso independiente | `python vants_bus/consumer.py` | Modo recomendado. Necesita un proceso que se mantenga encendido |
| Dentro de Flask | `main.py` llama a `iniciar()` al cargar la app y el consumidor corre en un hilo | Cuando la app corre como servidor persistente. No arranca en Vercel (variable `VERCEL`), porque las funciones serverless no mantienen hilos en segundo plano |
Como VANTS AI corre en Vercel, allí el consumidor no arranca dentro de Flask. Para tenerlo en marcha, lánzalo como proceso independiente:
```bash theme={null}
export VANTS_BUS_URL=https://vants-bus.vants.workers.dev
export VANTS_BUS_SECRET=
python vants_bus/consumer.py
```
En los dos modos, cada lectura espera hasta 20 segundos a que lleguen eventos (long-polling). La primera vez empieza desde el momento actual, sin procesar eventos antiguos. Tras un reinicio, continúa donde lo dejó.
## Publicar eventos desde los agentes
```python theme={null}
import os
from vants_bus.vants_bus_client import VantsBus, EVENTS
bus = VantsBus(os.environ["VANTS_BUS_URL"], "flask-agents", os.environ["VANTS_BUS_SECRET"])
bus.publish(EVENTS["HIGHLIGHTS_READY"], {"clipId": "abc", "game": "cs2"})
```
`EVENTS` es un diccionario con los 12 tipos del contrato. Qué proyecto consume cada tipo y cómo se firman las peticiones está en [Cómo se conectan los repositorios](/conexiones-entre-repositorios#vants-bus).
## Ejecutar en local
```bash theme={null}
pip install -r requirements.txt
python main.py # http://localhost:8000
```
`python main.py` arranca el servidor de desarrollo de Flask en el puerto 8000, con el modo debug activado. En un servidor persistente también puedes usar `gunicorn main:app`. Para probar el chat:
```bash theme={null}
curl -s -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-d '{"mensaje": "quiénes son los agentes"}'
```
## Despliegue en Vercel
El runtime Python de Vercel busca `api/index.py`, que importa la app de `main.py`, e instala las dependencias de `requirements.txt`. Las páginas, los estáticos y la API salen de la misma app.
# VantsBasededatos
Source: https://vantsgg.mintlify.app/repos/vants-base-de-datos
Migraciones SQL del esquema de VANTS en Neon: cómo crear una, probarla en la rama de cada PR y aplicarla en producción, más Hyperdrive y la cuota.
Repositorio con las migraciones SQL de la base de datos de VANTS (Neon Postgres, 69 tablas en producción). Aquí se crea y se prueba cualquier cambio de tablas antes de llevarlo a producción.
## Estructura
```text theme={null}
db/migrations/ 34 migraciones SQL idempotentes (01 a 34)
.github/workflows/
neon-migrate.yml aplica migraciones en producción
neon-pr-branch.yml crea y borra una rama Neon por PR
```
Las migraciones se ejecutan en orden alfabético y son idempotentes (`CREATE TABLE IF NOT EXISTS`, guards en PL/pgSQL), por lo que es seguro reaplicarlas.
## Flujo de trabajo
**1. Crear la migración**
Añade `db/migrations/35-mi-cambio.sql` en una rama.
**2. Abrir un PR**
Se crea la rama Neon `pr-` con las migraciones aplicadas y un comentario con la cadena de conexión enmascarada.
**3. Verificar**
Prueba tu SQL contra esa rama, que es una copia de producción.
**4. Mergear a main**
`neon-migrate.yml` aplica el cambio en producción. La rama del PR se borra al cerrarlo.
## Áreas del esquema
| Área | Tablas de ejemplo |
| - | - |
| Bot y alertas | `bot_heartbeat`, `bot_settings`, `discord_channels`, `bot_alerts` |
| Cuentas y pagos | `vants_links`, `purchases`, `entitlements`, `audit_logs` |
| Ranked y FACEIT | `series_ranked`, `faceit_matches`, `faceit_webhook_events`, `rr_historial` |
| Matchmaking | `mm_tickets`, `mm_matches`, `mm_match_players`, `mm_invites`, `mm_ready` |
| Plataforma de torneos | `pt_games`, `pt_torneos`, `pt_teams`, `pt_matches`, `pt_articulos`, `pt_players` |
| Agente | `agent_runs`, `agent_events`, `agent_comandos`, `agent_memoria`, `agent_archivos` |
| Bus de eventos | `vants_events`, `vants_consumers`, `vants_bus_dedup`, `vants_feed` |
Las columnas, los estados y las relaciones de cada área están en [Esquema de la base de datos](/referencia/esquema-base-de-datos).
## Variables opcionales
`NEON_PROJECT_ID` y `NEON_BRANCH_ID` permiten cambiar de proyecto o rama sin editar los workflows. La clave de API de Neon es un secreto a nivel de la organización VANTSNET.
## Hyperdrive
Una config de Cloudflare Hyperdrive (`vants-neon`) agrupa y cachea conexiones al pooler de Neon para reducir horas de cómputo. Se usa con el binding `HYPERDRIVE` y el driver `pg`.
## Cuota de Neon
El plan gratuito tiene horas de cómputo limitadas, y solo se consumen con el cómputo despierto. Para reducirlas:
1. Evita consumidores 24/7 con long-poll al bus.
2. Usa el mínimo de *Suspend delay* (5 min) y el tamaño mínimo de cómputo.
3. Cierra los PR para que sus ramas se borren.
4. Usa Hyperdrive para lecturas repetidas.
# vants-clips
Source: https://vantsgg.mintlify.app/repos/vants-clips
Repositorio de datos con los clips destacados de VANTS: cada clip es un release con vídeo y miniatura, y manifest.json es el índice que muestra la web.
Almacén de los clips que los jugadores destacan desde la arena. Se gestiona automáticamente vía API y se muestra en la página de clips de la plataforma.
## Contenido
| Elemento | Uso |
| - | - |
| Releases | Cada clip es un release con dos archivos: el vídeo (`clip.`) y la miniatura (`thumb.jpg`). Un clip publicado no se modifica. |
| `manifest.json` | Índice de los clips destacados que muestra la web |
| `README.md` | Descripción del repositorio |
> [!NOTE]
>
> Este repositorio es público y no contiene código, solo datos que la plataforma actualiza.
## Cómo se actualiza
La web escribe en este repositorio a través del Worker de [Vantsgames](/repos/vantsgames), con la ruta `/api/clips/featured`:
* `GET` devuelve el índice de clips publicados. Es público.
* `POST`, con sesión, crea el release con el vídeo y la miniatura y añade el clip al índice.
* `DELETE`, con sesión y solo para el autor del clip, lo quita del índice y borra su release.
Los campos, los límites y los errores están en [Clips destacados](/referencia/api-publica#clips-destacados).
Los clips se ven en la sección Clips de la web: [https://vants.vants.workers.dev/clips](https://vants.vants.workers.dev/clips)
# vants-dev-launcher
Source: https://vantsgg.mintlify.app/repos/vants-dev-launcher
Versión ampliada de VANTS con las páginas de torneos, la home en Astro, el launcher Electron, el servidor MCP, Docker y sus workflows de GitHub Actions.
Plataforma de torneos estilo strafe.com: calendario por juego y estado, partidas en vivo, ranking, brackets, equipos, jugadores, noticias, búsqueda, tienda y membresías. Comparte con [Vantsgames](/repos/vantsgames) el Worker, las Neon Functions, el bot y el agente, y añade la home en Astro, el launcher de escritorio, el servidor MCP del proyecto y servicios en Docker.
Web: [https://vants.vants.workers.dev](https://vants.vants.workers.dev)
## Estructura
| Ruta | Contenido |
| - | - |
| Raíz y `public/` | Páginas HTML (inicio, equipos, torneos, partidos, ranking, clips, membresías, tienda) |
| `vants-server/` | Worker de Cloudflare y lógica compartida |
| `webapp/` | Astro con islas React (Explore) y Vue + Three (Emblema3D) |
| `neon-functions/` | `vantsdata`, `mm`, `vantsbot`, `linker`, `supervisor`, Stripe, Discord |
| `services/` | Servicio de agente y matchmaking |
| `bot/` | Bot ORION de Discord (Python). Ver [Bot de Discord](/referencia/bot-de-discord) |
| `desktop-electron/` | Launcher Electron con auto-update |
| `android/` | App Android |
| `mcp/` | Servidor MCP stdio con 4 herramientas |
| `monitor/` | Monitor de disponibilidad |
| `db/migrations/` | Migraciones SQL (01 a 34) |
| `docs/` | Guías: testing, despliegue, troubleshooting, configuración |
| `product/`, `architecture/`, `research/` | Requisitos, diseño y análisis de referencia |
> [!WARNING]
>
> La web estática está repetida en la raíz, `public/` y `vants-server/public/`, y las tres copias deben ser idénticas. `node scripts/build-webapp.mjs` copia el build de `webapp/` a las tres y `node scripts/build-webapp.mjs --check` comprueba que coinciden. El workflow `fullstack` lo vigila.
## Requisitos
* Node 20 o superior.
* Cuenta de Cloudflare con el Worker `vants`.
* Proyecto Neon y tenant de Auth0.
## Workflows de GitHub Actions
`tests`, `neon-migrate`, `neon-deploy-functions`, `cloudflare-deploy`, `fullstack`, `agente`, `faceit-sync`, `faceit-xp`, `monitor`, `mejoras-discord`, `desktop-electron`, `windows-build` y `android-build`. Hay también `bootstrap-copiar-neon`, que se lanza a mano y copia la clave de Neon (`NEON_API_KEY`) a los secretos de la organización y de los repositorios que la necesitan.
Al hacer push a `main`, cada workflow se lanza según los archivos que cambien. Consulta [Arquitectura general](/arquitectura).
## Docker
`docker-compose.yml` levanta un `api-gateway` (Express, puerto 8080) y `matchmaking` (puerto 8081).
# vants-faceit-webhook
Source: https://vantsgg.mintlify.app/repos/vants-faceit-webhook
Worker que recibe los webhooks de FACEIT, los guarda en Neon y los publica en VANTS-BUS: endpoints, mapeo de eventos, garantías, secretos y despliegue.
Backend de VANTS para recibir webhooks de FACEIT (App Studio) e integrarlos con Neon PostgreSQL. Corre como Cloudflare Worker y usa `@neondatabase/serverless`. Cada evento verificado se guarda en la tabla `faceit_webhook_events` y, si tiene equivalente en el bus de eventos, se publica allí para que el resto de la plataforma reaccione.
## Endpoints
| Método | Ruta | Descripción |
| - | - | - |
| `POST` | `/api/webhooks/faceit` | Valida el secreto, guarda el evento en `faceit_webhook_events` y responde `200`. |
| `GET` | `/api/webhooks/faceit` | Sondeo de disponibilidad (`200`). |
| `GET` | `/healthz` | Estado del servicio e indica si el bus está configurado. |
## Validación del secreto
El `POST` solo se acepta si incluye el secreto compartido con FACEIT. Los nombres exactos de header o parámetro que acepta el Worker están en el código del repositorio.
## Secrets del Worker
El Worker usa secretos para validar a FACEIT, conectarse a Neon y firmar las peticiones al bus de eventos.
> [!NOTE]
>
> Ninguno vive en el repo. Para cambiarlos: `npx wrangler secret put `.
## Puente al bus de eventos (VANTS-BUS)
Cada webhook verificado se guarda en Neon. Si el bus está configurado, además se publica en el bus central para que la web, el launcher y los agentes reaccionen en vivo.
| Evento FACEIT | Evento del bus | Reacción esperada |
| - | - | - |
| `match_status_finished` | `match.finished` | Ranking en vivo, resumen por agentes, aviso en el launcher. |
| `match_object_created` | `match.scheduled` | La agenda de la arena se actualiza. |
| Cancelaciones | `match.canceled` | Se retira de la agenda. |
Los eventos sin mapeo se ignoran.
### Garantías
* La respuesta a FACEIT no cambia: primero se guarda en Neon y se responde `200`. La publicación al bus va en background (`ctx.waitUntil`).
* Deduplicación por `event_id` con la tabla `vants_bus_dedup` (se crea sola en Neon).
* 3 reintentos con backoff hacia el bus.
### Consumir eventos desde otros proyectos
Otros proyectos leen los eventos del bus con peticiones firmadas con HMAC por proyecto. La implementación está en `vants-bus/` (ver [Vantsgames](/repos/vantsgames)).
## Despliegue
```bash theme={null}
npm install
npm run deploy
```
# Vants-Proteger
Source: https://vantsgg.mintlify.app/repos/vants-proteger
SPA estática de VANTS con Auth0 y Neon: app Vue en web/, versión anterior del bot de Discord, app Android (TWA), Neon Functions y sus workflows.
Sitio estático de VANTS con autenticación **Auth0** y datos en **Neon**. Hay una versión nueva en Vue dentro de `web/`. Antes se llamaba VantsSeguridad. Las medidas de seguridad de toda la plataforma no están en este repositorio, sino en [Seguridad](/referencia/seguridad).
## Componentes
| Ruta | Contenido |
| - | - |
| Raíz | SPA estática: HTML, JS y CSS (`app.js`, `auth0-config.js`, `faceit.js`, `admin.js`) |
| `web/` | App Vue con Vite: vistas de inicio, jugadores, torneos, ranking, planes, calendario, cuenta y admin. Incluye pruebas (Vitest y Playwright) |
| `bot/` | Bot de Discord en Python (versión anterior; ver [Bot de Discord](/referencia/bot-de-discord)) |
| `android/` | App Android (TWA) |
| `neon-functions/` | `discord-notify`, `steam-login`, `stripe-webhook` |
La app de escritorio de VANTS es el launcher Electron de [vants-dev-launcher](/repos/vants-dev-launcher#estructura).
## Hosting y CI
Se publica como sitio estático (GitHub Pages y Netlify). Workflows: `web-ci`, `deploy-web` y `android-build`.
## Integraciones
* **Auth0**: identidad.
* **Neon**: PostgreSQL con Data API.
* **Stripe**: pagos de planes.
* **Steam**: login con OpenID.
* **FACEIT**: vinculación de cuenta y partidas.
* **Discord**: bot y notificaciones.
## Más información
El repositorio incluye `ARCHITECTURE.md` con diagramas completos, `web/ADMIN.md` y `bot/SETUP.md`.
# Vantsgames
Source: https://vantsgg.mintlify.app/repos/vantsgames
Repositorio principal de VANTS: carpetas, rutas públicas, despliegue del Worker, workflows de GitHub Actions, secretos y puesta en marcha del bot.
Repositorio principal de la plataforma de torneos VANTS. Antes se llamaba Playvants-web, y el código conserva ese nombre en algunos identificadores, como el proyecto `playvants-web` del bus de eventos. Contiene el Worker de Cloudflare que sirve la web y la API, las Neon Functions, el bot de Discord, el agente VANTS, el bus de eventos y los workflows que lo prueban y despliegan. La [API pública](/referencia/api-publica) y el [bot de Discord](/referencia/bot-de-discord) se documentan a partir del código de este repositorio.
## Estructura
| Ruta | Contenido |
| - | - |
| `vants-server/` | Worker de Cloudflare (`worker.js`), lógica compartida (`plataforma-core.js`) y frontend en `public/` |
| `neon-functions/` | Funciones: `vantsdata`, `mm`, `vantsbot`, `linker`, `supervisor`, `stripe-checkout`, `stripe-webhook`, `discord-notify` |
| `services/agent-service/` | Servicio de agente con LLM (cerebro, herramientas, memoria, sandbox) |
| `bot/` | Bot de Discord en Python |
| `vants-bus/` | Bus de eventos y worker consumidor |
| `db/migrations/` | Migraciones SQL idempotentes (01 a 34) |
| `scripts/` | Pruebas, sincronización con FACEIT, scraper de vlr.gg |
| `vlr-results/` | App en Python para resultados |
| `architecture/`, `product/`, `docs/` | Diseño, requisitos y guías internas |
| `android/` | App Android |
## Rutas públicas
El Worker expone rutas limpias como `/es/tournaments/:juego/:estado/` (también `/en/...`) y una API JSON pública (`/api/tournaments`, `/api/teams`, `/api/articles`, `/api/search`, entre otras). Las rutas, los parámetros y las respuestas están en [API pública](/referencia/api-publica).
## Despliegue
El Worker sirve el frontend desde `vants-server/public/`. Verificación posterior al despliegue: las páginas principales deben responder `200` con un User-Agent de navegador. Sin User-Agent la protección contra bots responde `403`.
Para revertir: `git revert ` y push a `main`; el CI vuelve a desplegar.
Los workflows que despliegan y las precauciones antes de fusionar a `main` están en [Seguridad](/referencia/seguridad#despliegues-y-ci).
## Workflows
Los workflows están en `.github/workflows/`. Todos se pueden lanzar también a mano desde la pestaña Actions de GitHub.
| Workflow | Cuándo se ejecuta | Qué hace |
| - | - | - |
| `tests` | Push o PR a `main` que toque `scripts/`, `vants-server/`, `neon-functions/` o `db/` | Pruebas automáticas |
| `cloudflare-deploy` | Push a `main` que toque `vants-server/` | Despliega el Worker `vants` |
| `neon-migrate` | Push a `main` que toque `db/migrations/` | Aplica las migraciones en Neon |
| `neon-deploy-functions` | Push a `main` que toque `neon-functions/` | Despliega las Neon Functions |
| `android-build` | Push a `main` o PR que toque `android/` | Compila la app Android |
| `mejoras-discord` | Cada push a `main` | Avisa en Discord de los cambios |
| `agente` | Cada 30 minutos y cuando Vercel avisa de un despliegue | Ejecuta el agente VANTS: revisa la web, el webhook de FACEIT, los workflows y Neon, y relanza una vez lo que falle |
| `monitor` | Cada 30 minutos | Comprueba la web y `vantsdata`, y avisa en Discord si cambia su estado |
| `faceit-xp` | Cada 10 minutos | Procesa las partidas de FACEIT y actualiza la XP y el rango |
| `faceit-sync` | Cada día a las 05:17 UTC | Sincroniza los torneos reales de FACEIT con las tablas `pt_` |
`cloudflare-deploy`, `neon-migrate` y `neon-deploy-functions` comparten la cola `deploy-prod`, así que nunca se ejecutan a la vez.
## Secretos necesarios
Las credenciales de Cloudflare, Neon, FACEIT, Steam, HenrikDev, Discord y del servicio de agente se configuran como secrets del repositorio (GitHub) o del Worker (Cloudflare). No viven en el código. Cómo guardarlos y rotarlos: [Seguridad](/referencia/seguridad#secretos).
> [!WARNING]
>
> Nunca subas secretos ni archivos `.env` al repositorio.
## Bot de Discord
Las variables de entorno están en `bot/.env.example`. Además del token del bot y la cadena de conexión de Neon (`sslmode=require`), el bot usa:
| Variable | Uso |
| - | - |
| `DISCORD_GUILD_ID` | Servidor donde se registran los comandos |
| `PORT` | Puerto del health check (8080 por defecto) |
```bash theme={null}
cd bot
pip install -r requirements.txt
cp .env.example .env
python main.py
```
Los comandos viven en `bot/commands/`: análisis, comunidad, informes, moderación, perfil, verificar, vincular y agente. Cada comando, con sus permisos, está en [Bot de Discord](/referencia/bot-de-discord).
# VANTSNET
Source: https://vantsgg.mintlify.app/repos/vantsnet
Prototipo de red de estadísticas y plataforma competitiva en Next.js y Prisma: Valorant, Steam, torneos, equipos, Discord, FACEIT, puesta en marcha y CI.
Prototipo de una red de estadísticas de videojuegos y plataforma competitiva: perfiles de jugadores, historial de partidas y scoreboards, con Valorant como primer juego, más torneos, equipos, inscripciones y resultados. Está en la cuenta de GitHub feispla ([feispla/VANTSNET](https://github.com/feispla/VANTSNET)), no en la organización VANTSNET aunque se llame igual, y no comparte código ni servicios con el resto de repositorios: no usa Auth0. Cada push a una rama se despliega en Prisma Cloud con el workflow `prisma-deploy.yml`: `main` va a producción y las demás ramas crean un preview con su propia base de datos.
Stack: Next.js 16 (App Router), React 19, TypeScript, Tailwind 4, componentes de shadcn/ui, Zustand, Prisma ORM 8 con PostgreSQL (el contrato vive en `src/prisma/contract.prisma` y la conexión en `prisma.config.ts`), zod para validar las entradas de la API y Vitest para los tests. La versión del paquete es la 0.2.0.
> [!NOTE]
>
> Riot reserva los datos de partidas de Valorant (VAL-MATCH-V1) para claves de producción. Con una clave de desarrollo la app verifica cuentas con `account-v1` y explica el bloqueo; sin clave solo funciona el modo demo.
## Qué incluye
* **Modo demo**: busca `Demo#VANTS` sin ninguna clave. Son 8 partidas simuladas de forma determinista, con estadísticas agregadas (winrate, K/D, ACS, ADR y porcentaje de headshots) y scoreboard por partida.
* **Valorant con la API de Riot**: resuelve Riot IDs reales (`Nombre#TAG`) y, con clave de producción, guarda y agrega las últimas 8 partidas del jugador.
* **Steam**: `GET /api/steam/resolve` devuelve un perfil de Steam y sus 10 juegos con más horas.
* **Plataforma competitiva**: torneos con estados (borrador, inscripción, en juego, completado o cancelado), equipos con plantilla, inscripciones sin duplicados, partidas con resultados validados y un registro de auditoría de cada cambio. Las lecturas son públicas y los cambios exigen el token de administración.
* **Discord y FACEIT**: vinculación de cuentas de Discord con OAuth2 y consulta de jugadores de FACEIT. Sin credenciales responden `503` con el código `NOT_CONFIGURED` en lugar de fallar.
* **Estado del servicio**: `GET /api/health` y la pestaña «Estado» indican si la base de datos responde y qué integraciones están configuradas, sin mostrar ninguna clave.
## Interfaz
Todo está en una sola página (`/`) que cambia de vista sin recargar:
* **Buscador**: un Riot ID y una región. El botón «API key» permite pegar una clave de Riot propia (`RGAPI-...`). Se guarda en el navegador y se envía al backend en la cabecera `x-riot-key`, que tiene prioridad sobre `RIOT_API_KEY`.
* **Perfil y partida**: estadísticas agregadas, resumen por agente, últimas partidas y scoreboard.
* **Competitivo** (botón con el trofeo en la cabecera): pestañas Torneos, Equipos y Estado, con datos de la base de datos. El detalle de un torneo muestra sus inscripciones y partidas.
Con «Soy admin» se introduce el token de administración, que solo se recuerda en esa pestaña del navegador. Con él aparecen las acciones de gestión: crear torneos (empiezan en borrador) y equipos, cambiar el estado de un torneo, inscribir equipos, aprobar o rechazar inscripciones, programar partidas y reportar resultados.
Discord y FACEIT todavía no tienen botones en la interfaz: se usan por API. Al volver de Discord, la página muestra el resultado de la vinculación (`?auth=linked` o `?auth=error`) y limpia la URL.
## Estructura
```text theme={null}
src/app/page.tsx interfaz: buscador, perfil, partida y vista Competitivo en una sola página
src/app/api/valorant/ GET profile y match/{matchId}
src/app/api/steam/resolve/ GET /api/steam/resolve
src/app/api/tournaments/ torneos, inscripciones y partidas
src/app/api/teams/ equipos y miembros
src/app/api/admin/audit/ GET /api/admin/audit
src/app/api/auth/discord/ start, callback, status y link de la vinculación con Discord
src/app/api/faceit/player/ GET /api/faceit/player
src/app/api/health/ GET /api/health
src/components/vantsnet/ vistas: search-view, profile-view, match-view, hub-view, tournament-detail y agent-avatar
src/components/ui/ componentes de shadcn/ui
src/lib/store.ts estado global de la interfaz (Zustand)
src/lib/valorant/riot.ts cliente de la API de Riot, caché en base de datos y agregación
src/lib/valorant/demo-data.ts datos del modo demo
src/lib/valorant/types.ts tipos de las respuestas
src/lib/steam/steam.ts cliente de la Steam Web API
src/lib/discord.ts OAuth2 de Discord: state firmado, intercambio del código y datos del usuario
src/lib/faceit.ts cliente de la FACEIT Data API v4
src/lib/bus/ cliente de VANTS-BUS (vants-bus.ts) y publicación de eventos (publish.ts)
src/lib/admin.ts comprobación del token de administración
src/lib/audit.ts escritura del registro de auditoría
src/lib/validators.ts esquemas zod de las entradas de la API
src/lib/db.ts punto único de acceso a la base de datos que usan las rutas
src/prisma/ contrato de Prisma ORM 8 (`contract.prisma`), cliente (`db.ts`) y artefactos emitidos
migrations/ migraciones versionadas del esquema
module.mjs y service.mjs despliegue de la aplicación en Prisma Cloud
tests/ tests unitarios (Vitest): unit.test.ts y bus.test.ts
docs/ arquitectura, base de datos, entorno, integraciones, despliegue, tests y CI
.github/ workflows y configuración de Dependabot
scripts/ regenera el diagrama de arquitectura (HTML y Playwright)
```
## Puesta en marcha
```bash theme={null}
bun install # o npm install
cp .env.example .env # opcional: añade las claves que necesites
bun run db:update # aplica el contrato a la base de datos
bun run dev # o npm run dev; abre http://localhost:3000
```
Busca `Demo#VANTS` para probar la app sin claves. Para validar un cambio antes de subirlo:
```bash theme={null}
bun run lint # ESLint
bun run typecheck # tsc --noEmit
bun run test # Vitest
bun run build # build de producción
```
### Variables de entorno
La referencia completa está en `docs/ENVIRONMENT.md`.
| Variable | Obligatoria | Uso |
| - | - | - |
| `DATABASE_URL` | Sí | Base de datos PostgreSQL: Prisma Postgres, Neon o cualquier PostgreSQL accesible. En Prisma Cloud la plataforma la inyecta en runtime y no hace falta definirla |
| `RIOT_API_KEY` | No | Clave de Riot. Sin ella solo funciona el modo demo |
| `STEAM_API_KEY` | No | Clave de la Steam Web API. Habilita `/api/steam/resolve` |
| `ADMIN_TOKEN` | Para gestionar torneos y equipos | Valor que se envía en la cabecera `X-Admin-Token`. Sin él, las rutas de administración responden `503` |
| `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET` y `DISCORD_OAUTH_STATE_SECRET` | Para Discord | Aplicación OAuth2 de Discord y secreto con el que se firma el parámetro `state` |
| `DISCORD_REDIRECT_URI` | No | URL de retorno fija. Si falta, se usa `/api/auth/discord/callback` |
| `FACEIT_API_KEY` | Para FACEIT | Clave de servidor de la FACEIT Data API v4 |
| `VANTS_BUS_URL` y `VANTS_BUS_SECRET` | Para el bus | URL pública del bus y secreto HMAC del proyecto `vantsnet`. Sin ellas, la publicación queda apagada |
En `.env.example` todas las claves están vacías. Si no tienes una, déjala así: la API responde con el aviso de que falta la clave en lugar de llamar al servicio externo.
`.env`, la carpeta `db/` y los archivos `*.db` están en `.gitignore`.
### Scripts
| Script | Qué ejecuta |
| - | - |
| `dev` | `next dev -p 3000` y guarda la salida en `dev.log` |
| `build` | `next build` con salida `standalone` y copia `.next/static` y `public/` a `.next/standalone/` |
| `start` | `.next/standalone/server.js` con Bun en modo producción y guarda la salida en `server.log` |
| `lint` | `eslint .` |
| `typecheck` | `tsc --noEmit` |
| `test` | `vitest run` |
| `test:watch` | `vitest` en modo observación |
| `db:emit` | `prisma contract emit` |
| `db:update` | `prisma db update` |
| `db:sign` | `prisma db sign` |
| `db:plan` | `prisma migration plan` |
| `db:migrate` | `prisma db migrate --advance-ref db` |
## Endpoints
Todos responden en JSON. Los errores tienen la forma `{ "error": "", "code": "" }`; algunos solo traen `error`.
### Estadísticas de Valorant y Steam
Son `GET` y públicos.
| Ruta | Parámetros | Devuelve |
| - | - | - |
| `/api/valorant/profile` | `riotId` (obligatorio, `Nombre#TAG`) y `region`: `europe` (por defecto), `americas` o `asia` | Jugador, estadísticas agregadas, resumen por agente y últimas partidas |
| `/api/valorant/match/{matchId}` | `region` (opcional, `europe` por defecto) | Scoreboard: mapa, modo, fecha, duración y equipos con K/D/A, ACS, ADR y porcentaje de headshots de cada jugador |
| `/api/steam/resolve` | `query`: SteamID64, URL de perfil (`/profiles/` o `/id/`) o nombre de la vanity URL | SteamID64, perfil, número de juegos y los 10 juegos con más horas |
```bash theme={null}
curl "http://localhost:3000/api/valorant/profile?riotId=Demo%23VANTS®ion=europe"
```
El `#` del Riot ID va codificado como `%23`.
* `Demo#VANTS`, sin distinguir mayúsculas, siempre devuelve datos demo, haya o no clave.
* Los IDs de partida `demo-match-` devuelven el scoreboard demo.
* Con clave, una partida que ya está guardada se sirve desde la base de datos sin volver a llamar a Riot.
* Si los detalles de juego de un perfil de Steam son privados, la respuesta trae el perfil con la lista de juegos vacía.
Un `riotId`, una `region` o una `query` ausentes o con formato inválido devuelven `400` solo con `error`, y un fallo inesperado devuelve `500`. El resto de errores:
| Código | Cuándo | HTTP |
| - | - | - |
| `NO_KEY` | No hay clave de Riot o de Steam | `400` en Valorant, `502` en Steam |
| `INVALID_KEY` | Riot o Steam rechazan la clave | `403` |
| `FORBIDDEN_PRODUCT` | La clave de Riot es válida pero no da acceso a los endpoints de Valorant | `403` en perfil, `502` en partida |
| `NOT_FOUND` | Jugador, partida o perfil de Steam no encontrado, o entrada de Steam no reconocida | `404` |
| `RATE_LIMIT` | Límite de peticiones alcanzado | `429` |
| `NO_DATA` | El jugador no tiene partidas recientes | `502` |
| `NETWORK` | Fallo de conexión o estado inesperado de la API externa | `502` |
Con `FORBIDDEN_PRODUCT`, la respuesta del perfil incluye `verifiedAccount` con la cuenta que `account-v1` ya verificó.
### Plataforma competitiva
Las rutas marcadas como admin exigen la cabecera `X-Admin-Token` con el valor de `ADMIN_TOKEN`, y cada cambio queda en el registro de auditoría.
| Método y ruta | Acceso | Qué hace |
| - | - | - |
| `GET /api/tournaments` | Público | Lista hasta 100 torneos, del más reciente al más antiguo, con el número de equipos inscritos y de partidas. Filtro opcional `?status=` |
| `POST /api/tournaments` | Admin | Crea un torneo. Empieza en `draft` y el slug se genera a partir del nombre |
| `GET /api/tournaments/{id}` | Público | Detalle con las inscripciones (equipo y plantilla) y las partidas ordenadas por ronda |
| `PATCH /api/tournaments/{id}` | Admin | Cambia el nombre, el estado, la descripción, las reglas o las fechas |
| `POST /api/tournaments/{id}/registrations` | Admin | Inscribe un equipo: `teamId` y `notes` opcional. La inscripción empieza en `pending` |
| `PATCH /api/tournaments/{id}/registrations` | Admin | Aprueba o rechaza una inscripción: `registrationId` y `action` (`approve` o `reject`) |
| `POST /api/tournaments/{id}/matches` | Admin | Programa una partida: `round` (de 1 a 16), `slot` (por ejemplo `QF1`), `homeTeamId`, `awayTeamId` y `playedAt`, todos opcionales |
| `PATCH /api/tournaments/{id}/matches` | Admin | Reporta un resultado: `matchId` con `homeScore`, `awayScore` o `status` |
| `GET /api/teams` | Público | Lista hasta 100 equipos con su plantilla y su número de inscripciones. Filtro opcional `?game=` |
| `POST /api/teams` | Admin | Crea un equipo con hasta 12 miembros opcionales |
| `POST /api/teams/{teamId}/members` | Admin | Añade un miembro: `nickname` y `role` |
| `DELETE /api/teams/{teamId}/members/{memberId}` | Admin | Quita un miembro |
| `GET /api/admin/audit` | Admin | Últimos registros de auditoría: 50 por defecto y hasta 200 con `?take=` |
```bash theme={null}
curl -X POST http://localhost:3000/api/tournaments \
-H "Content-Type: application/json" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"name": "Copa VANTS", "maxTeams": 8}'
```
Valores admitidos:
| Campo | Valores |
| - | - |
| Estado del torneo | `draft`, `registration`, `running`, `completed` y `cancelled` |
| Formato del torneo | `single_elimination` (por defecto), `double_elimination` y `round_robin` |
| Rol de un miembro | `player` (por defecto), `captain`, `coach` y `substitute` |
| Estado de una inscripción | `pending`, `approved` y `rejected` |
| Estado de una partida | `scheduled`, `live`, `completed` y `forfeit` |
El juego por defecto es `valorant` y un torneo admite de 2 a 128 equipos (8 por defecto).
Reglas que aplica la API:
* Solo se inscriben equipos en torneos en estado `registration` y antes de `registrationDeadline`. Si no, responde `409`.
* Un torneo completo responde `409`, y un equipo ya inscrito, `409` con `DUPLICATE_REGISTRATION`.
* Para completar una partida hay que enviar `homeScore` y `awayScore`, y se guarda la hora en `playedAt`.
* Una partida `completed` o `forfeit` ya no se puede modificar (`409`). Para corregir un resultado se crea otra partida.
* El nombre de un equipo es único por juego (`409` con `DUPLICATE_TEAM`) y el apodo, único dentro del equipo (`409` con `DUPLICATE_MEMBER`).
* Si el slug de un torneo ya existe, se le añade un sufijo: `-2`, `-3`...
Sin `ADMIN_TOKEN` en el servidor, las rutas admin responden `503` con `ADMIN_NOT_CONFIGURED`; con un token ausente o incorrecto, `401` con `ADMIN_INVALID`. Una entrada inválida devuelve `400` con el primer error de validación y el campo afectado, y un torneo, equipo, inscripción o partida inexistentes devuelven `404`.
### Discord y FACEIT
| Método y ruta | Qué hace |
| - | - |
| `GET /api/auth/discord/start` | Redirige a la autorización de Discord con el scope `identify` |
| `GET /api/auth/discord/callback` | Recibe la respuesta de Discord, vincula la cuenta y vuelve a `/?auth=linked` o a `/?auth=error&reason=` |
| `GET /api/auth/discord/status` | Indica si Discord está configurado: `configured` y `redirectUriReady` |
| `DELETE /api/auth/discord/link` | Desvincula una cuenta de Discord a partir de su `providerId`. Exige `X-Admin-Token` |
| `GET /api/faceit/player?nickname=` | Jugador de FACEIT, con su nivel y ELO por juego, y sus 5 últimas partidas |
* La vinculación con Discord guarda la cuenta en `ProviderIdentity` y crea un usuario interno si no existía. Repetirla actualiza los datos en lugar de duplicarlos.
* El parámetro `state` del flujo va firmado y caduca a los 10 minutos.
* Al desvincular una cuenta, también se borra el usuario interno si se queda sin otras cuentas vinculadas ni equipos.
* Si falla la consulta del historial de FACEIT, la respuesta trae el perfil con la lista de partidas vacía.
Errores de FACEIT: `400` con `BAD_INPUT` si el nickname tiene menos de 2 caracteres, `503` con `NOT_CONFIGURED` sin clave, `404` con `NOT_FOUND`, `429` con `RATE_LIMIT`, y `502` con `INVALID_KEY` o `NETWORK`.
### Publicación en VANTS-BUS
Las mutaciones de la plataforma competitiva publican sus eventos al bus del ecosistema con identidad `vantsnet`, mediante el cliente de `src/lib/bus/` y el mismo contrato de firma que los demás proyectos. La tabla muestra qué endpoint publica qué evento:
| Endpoint | Evento publicado |
| - | - |
| `POST /api/tournaments` (al crear el torneo) | `tournament.inscriptions_opened` |
| `PATCH /api/tournaments/{id}` (pasa el torneo a `registration`) | `tournament.inscriptions_opened` |
| `POST /api/tournaments/{id}/registrations` | `inscription.created` |
| `POST /api/tournaments/{id}/matches` (con `playedAt`) | `match.scheduled` |
| `PATCH /api/tournaments/{id}/matches` (cierra como `completed`) | `match.finished` |
El envío no bloquea la respuesta y un fallo del bus solo deja una línea en el log: la mutación ya está guardada. Si faltan `VANTS_BUS_URL` o `VANTS_BUS_SECRET`, la publicación queda apagada y la API funciona igual. Los consumidores son flask-agents y VANTS HUB, y `vants-consumer-playvants` copia los eventos a `vants_feed`.
### Estado del servicio
`GET /api/health` responde `200` con `"status": "healthy"` si la base de datos responde, y `503` con `"status": "degraded"` si no. `checks.integrations` indica con `true` o `false` si Riot, Steam, Discord y FACEIT están configurados, y `checks.admin`, si lo está `ADMIN_TOKEN`. Nunca devuelve las claves.
## Cómo se obtienen los datos de Riot
**1. Resolver la cuenta**
`account-v1` convierte `Nombre#TAG` en un `puuid` y guarda o actualiza al jugador en `Player`.
**2. Pedir el historial**
Se piden a VAL-MATCH-V1 los IDs de las últimas 8 partidas del jugador (`MATCH_COUNT`).
**3. Guardar cada partida una sola vez**
Las partidas que no están en `Match` se descargan y se guardan con las estadísticas de cada jugador en `MatchPlayerStat`, y cada jugador visto se registra en `Player`. Los nombres de mapas y agentes salen de VAL-CONTENT-V1 (`locale=es-ES`), que se guarda en memoria 24 horas.
**4. Agregar desde la base de datos**
`buildProfile` calcula winrate, K/D, ACS, ADR, porcentaje de headshots y el resumen por agente a partir de lo guardado.
Las llamadas a Riot y a Steam se hacen desde el servidor, sin caché HTTP y con un timeout de 12 segundos; las de FACEIT, con un timeout de 10 segundos. Riot recibe la clave en la cabecera `X-Riot-Token`.
## Base de datos
`src/prisma/contract.prisma` define once modelos:
| Modelo | Clave única | Contenido |
| - | - | - |
| `Player` | `puuid` | Riot ID (`gameName` y `tagLine`), región, juego y fecha de la última consulta (`lastFetchedAt`) |
| `Match` | `externalId`, el ID de partida de Riot | Mapa, modo, inicio, duración, rondas, equipos (`teamsJson`) y la respuesta original de Riot (`raw`) |
| `MatchPlayerStat` | `id`, con índices en `matchId` y `puuid` | Estadísticas de un jugador en una partida: agente, K/D/A, puntuación, daño, impactos por zona y si ganó |
| `User` | `id` | Usuario interno con su nombre visible, sus cuentas vinculadas y sus equipos |
| `ProviderIdentity` | `provider` y `providerId` | Cuenta externa vinculada (Discord, FACEIT, Riot o Steam): nombre, avatar y fecha de la última sincronización |
| `Team` | `name` y `game` | Equipo: nombre, tag, juego, región, logo y descripción |
| `TeamMember` | `teamId` y `nickname` | Miembro de un equipo con su rol, enlazado opcionalmente a un `User` |
| `Tournament` | `slug` | Torneo: juego, formato, estado, máximo de equipos, descripción, reglas, fecha de inicio y cierre de inscripción |
| `TournamentRegistration` | `tournamentId` y `teamId` | Inscripción de un equipo en un torneo, con su estado y notas |
| `TournamentMatch` | `id`, con índice en `tournamentId` y `round` | Partida de torneo: ronda, slot, equipos local y visitante, marcador, estado y fecha |
| `AuditLog` | `id`, con índices en `entity` y `entityId` y en `createdAt` | Quién hizo el cambio (`actor`), qué acción, sobre qué entidad y el detalle en JSON |
Los estados, formatos y roles se guardan como texto y los valida zod en la frontera de la API. Con `onDelete: Cascade`, borrar una partida de Riot borra sus estadísticas; borrar un torneo, sus inscripciones y partidas; borrar un equipo, sus miembros e inscripciones, y borrar un usuario, sus cuentas vinculadas.
> [!NOTE]
>
> El contrato no declara bloques `generator` ni `datasource`: la conexión vive en `prisma.config.ts` y la base de datos es PostgreSQL (Prisma Postgres, Neon o cualquier PostgreSQL). Los artefactos `contract.json` y `contract.d.ts` se generan con `db:emit` y se commitean. El esquema SQL de `docs/ARQUITECTURA.md`, con la tabla `leaderboards`, es el diseño planificado y no el que usa el prototipo.
## Calidad y CI
| Workflow | Cuándo se ejecuta | Qué hace |
| - | - | - |
| `ci.yml` | Push y pull request a `main` | Con Bun y el lockfile congelado, ejecuta `lint`, `typecheck`, `test` y `build` |
| `codeql.yml` | Push y pull request a `main`, y cada lunes a las 05:30 UTC | Análisis de CodeQL con las consultas `security-extended` para JavaScript y TypeScript |
| `sonarcloud.yml` | Push a `main` y pull requests | Quality Gate de SonarCloud. Se omite mientras no exista el secreto `SONAR_TOKEN` |
| `prisma-deploy.yml` | En cada push a una rama y a mano | Despliega en Prisma Cloud: `main` va a producción y las demás ramas crean un preview con su propia base de datos |
Dependabot revisa cada semana las dependencias de npm y las GitHub Actions, con un máximo de 5 pull requests abiertas de cada tipo.
`.github/workflows/` contiene solo los cuatro workflows de la tabla anterior.
`tests/unit.test.ts` tiene 14 tests: el `state` firmado de Discord, los errores de FACEIT, los validadores de la API, las reglas de resultados de partidas y la generación de slugs. No hay tests de extremo a extremo automatizados; la verificación manual está en `docs/TESTING.md`.
## Integraciones
Estado según `docs/INTEGRATIONS.md`:
| Integración | Estado |
| - | - |
| Riot Games (Valorant) | Implementada y verificada |
| Steam Web API | Implementada y verificada |
| Discord OAuth2 | Implementada, pendiente de credenciales |
| FACEIT Data API v4 | Implementada, pendiente de credenciales |
| VANTS-BUS | Implementada, pendiente del secreto del proyecto `vantsnet` en el bus |
| Prisma Cloud | Activa: despliegue de la aplicación en cada push |
| Cloudflare | Documentada como alternativa (VPS con Cloudflare delante o Workers) |
## Documentos del repositorio
| Archivo | Contenido |
| - | - |
| `docs/ARQUITECTURA.md` | Arquitectura objetivo en cuatro capas (fuentes de datos, ingesta y colecta, núcleo backend y presentación) más operación, con diagrama Mermaid y esquema SQL |
| `docs/ROADMAP.md` | Plan de construcción en 5 fases con criterios de aceptación |
| `docs/DATABASE.md` | Base de datos PostgreSQL con Prisma ORM 8: contrato, comandos y migraciones |
| `docs/ENVIRONMENT.md` | Referencia de variables de entorno |
| `docs/INTEGRATIONS.md` | Configuración de Riot, Steam, Discord, FACEIT, VANTS-BUS y Cloudflare |
| `docs/DEPLOYMENT.md` | Despliegue en Prisma Cloud (opción activa) y alternativas en VPS con Cloudflare delante o Cloudflare Workers, checklist de producción y rollback |
| `docs/TESTING.md` | Qué cubren los tests y la verificación manual |
| `docs/CI-SETUP.md` | Instalación de CI, CodeQL y SonarCloud |
| `docs/SECURITY.md` | Modelo de seguridad del prototipo |
| `docs/KNOWN_ISSUES.md` | Incidencias conocidas y trabajo pendiente |
| `docs/PROJECT_AUDIT.md` y `docs/FINAL_COMPLETION_REPORT.md` | Auditoría e informe final de la fase de trabajo del 25/10/2026 |
| `docs/SOLICITUD-CLAVE-PRODUCCION.md` | Expediente para pedir a Riot la clave de producción de Valorant |
## Hoja de ruta
Estado de cada fase según el README:
| Fase | Entregable | Estado |
| - | - | - |
| 1 | Colector de Valorant con la API de Riot y esquema inicial | Prototipo funcional |
| 2 | API REST propia, caché y colas | Prototipo: API routes y caché en base de datos |
| 3 | Web de estadísticas: perfil, historial y partidas | Prototipo funcional |
| 4 | Multijuego con temas e ingesta de servidores | Planificado, con el cliente de Steam ya integrado |
| 5 | App Flutter, bot de Discord, API pública y Premium | Planificado |
# vantsoport
Source: https://vantsgg.mintlify.app/repos/vantsoport
Soporte, privacidad y términos de Vants, extensión de GitHub Copilot Chat que gestiona issues, consulta proyectos y ejecuta flujos de n8n con lenguaje natural.
Repositorio de soporte y documentación de **Vants**, una extensión de GitHub Copilot Chat que lleva un agente de IA al editor. No contiene código: incluye el README, la política de privacidad (`PRIVACY.md`), los términos de servicio (`TERMS.md`) y la licencia MIT. Repositorio: [feispla/vantsoport](https://github.com/feispla/vantsoport).
> [!NOTE]
>
> Vants no usa VANTS-BUS ni comparte código o servicios con la plataforma de VANTS.
## Qué hace Vants
* Crea, actualiza y clasifica issues desde Copilot Chat.
* Consulta el estado de los proyectos y genera resúmenes.
* Ejecuta flujos de automatización alojados en n8n (alertas, informes y monitoreo).
* Trabaja en español o en inglés.
Vants se conecta a un servidor MCP alojado en n8n. Cada petición se autentica con un token Bearer y se ejecuta contra los flujos que el propietario habilitó.
## Privacidad
Resumen de `PRIVACY.md`, actualizado el 7 de octubre de 2026:
| Tema | Qué dice |
| - | - |
| Datos de GitHub | Issues, repositorios y proyectos se consultan en tiempo real con la API de GitHub, solo con los permisos que autorizó el usuario. No se guardan fuera de la conversación |
| Conversaciones | Se procesan para responder y no se conservan en servidores propios al terminar la interacción |
| Analítica | No se recopilan datos de análisis, rastreo ni publicidad |
| Terceros | La API de GitHub (con autorización OAuth) y n8n Cloud (flujos de automatización, con tokens Bearer) |
| Revocar el acceso | Desde la configuración de aplicaciones de la cuenta de GitHub. Vants deja de consultar los datos de inmediato |
## Términos de servicio
Resumen de `TERMS.md`, actualizado el 7 de octubre de 2026:
* Úsala solo en repositorios y proyectos para los que tienes autorización. No la uses para acceder a datos de terceros sin permiso ni para actividades ilegales o abusivas.
* Se ofrece "tal cual", sin garantías de disponibilidad ni de exactitud de las respuestas de la IA.
* Las acciones que ejecuta (crear issues, cambiar estados, ejecutar flujos) se hacen bajo la responsabilidad del usuario y con sus permisos.
* Las respuestas de la IA pueden tener errores: verifica las acciones críticas antes de confirmarlas.
* Los cambios en los términos se publican en el mismo archivo.
## Soporte y contacto
* Soporte y preguntas de privacidad: [issues de vantsoport](https://github.com/feispla/vantsoport/issues)
* Autor: [@feispla](https://github.com/feispla)