# API de l’annuaire de bots Discordbot

Cette API permet à ton bot d’envoyer son nombre de serveurs (classements, graphiques) et de savoir si un utilisateur a voté pour lui.

- URL de base : `https://discordbot.fr/api/v1`
- Format : JSON (`Content-Type: application/json`)
- Version Markdown de cette page, à donner à une IA : https://discordbot.fr/bots/api.md

## Authentification

Génère un jeton sur la page de modification de ton bot (Dashboard → Mes bots → Modifier → API et statistiques). Il commence par `dbt_` et n’est affiché qu’une fois : en générer un nouveau invalide l’ancien.

Envoie-le dans l’en-tête `Authorization` (`Bearer ` est accepté mais facultatif) :

```http
Authorization: dbt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Le jeton est secret : garde-le côté serveur (variable d’environnement), jamais dans le code publié ni dans un navigateur. Un jeton ne fonctionne que pour son bot.

## Envoyer les statistiques

`POST /api/v1/bots/{bot_id}/stats` — jeton requis

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `server_count` | entier | oui | Nombre de serveurs (0 à 50 000 000) |
| `shard_count` | entier | non | Nombre de shards (1 à 100 000) |

Réponse : `200 { "ok": true }`

Envoie-les au démarrage puis toutes les 30 minutes environ. Limite : 30 requêtes par minute et par jeton.

```bash
curl -X POST "https://discordbot.fr/api/v1/bots/BOT_ID/stats" \
  -H "Authorization: $DISCORDBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"server_count": 1234, "shard_count": 1}'
```

### discord.js (Node 18+)

```js
async function postStats(client) {
  const res = await fetch(`https://discordbot.fr/api/v1/bots/${client.user.id}/stats`, {
    method: "POST",
    headers: { Authorization: process.env.DISCORDBOT_TOKEN, "Content-Type": "application/json" },
    body: JSON.stringify({ server_count: client.guilds.cache.size, shard_count: client.shard?.count ?? 1 }),
  });
  if (!res.ok) console.warn("discordbot.fr stats:", res.status, await res.text());
}

client.once("ready", () => {
  postStats(client);
  setInterval(() => postStats(client), 30 * 60 * 1000);
});
```

Avec plusieurs shards, envoie le total depuis un seul shard (par exemple la somme de `client.shard.fetchClientValues("guilds.cache.size")` sur le shard 0), sinon chaque shard écrase le nombre des autres.

### discord.py

```python
import os, aiohttp
from discord.ext import tasks

@tasks.loop(minutes=30)
async def post_stats():
    async with aiohttp.ClientSession() as session:
        await session.post(
            f"https://discordbot.fr/api/v1/bots/{bot.user.id}/stats",
            headers={"Authorization": os.environ["DISCORDBOT_TOKEN"]},
            json={"server_count": len(bot.guilds), "shard_count": bot.shard_count or 1},
        )

@post_stats.before_loop
async def before_post_stats():
    await bot.wait_until_ready()

# dans setup_hook ou on_ready : post_stats.start()
```

## Vérifier un vote

`GET /api/v1/bots/{bot_id}/check?userId={user_id}` — jeton requis

Indique si l’utilisateur a voté dans les 12 dernières heures (un vote toutes les 12 h). Limite : 120 requêtes par minute et par jeton.

```json
{ "voted": 1, "nextVoteAt": "2026-01-01T12:00:00.000Z" }
```

`voted` vaut `1` ou `0` ; `nextVoteAt` est la date du prochain vote possible, ou `null`.

## Webhook de vote

Renseigne une URL de webhook (HTTPS public) sur la page de modification : à chaque vote, nous envoyons un `POST` JSON. Si tu as défini un secret, il est envoyé tel quel dans l’en-tête `Authorization` : vérifie-le avant de faire confiance à la requête. Le bouton « Tester le webhook » envoie un appel de type `test`.

```json
{ "bot": "BOT_ID", "user": "USER_ID", "type": "upvote", "isWeekend": false, "query": "" }
```

`type` vaut `upvote` ou `test`. Réponds avec un code 2xx en moins de 5 secondes ; les redirections ne sont pas suivies et un échec n’est pas renvoyé.

## Endpoints publics

Sans jeton, CORS ouvert, uniquement pour les bots publiés.

- `GET /api/v1/bots/{bot_id}` — fiche du bot (cache 60 s) :

```json
{
  "id": "BOT_ID",
  "name": "My bot",
  "avatar_url": "https://cdn.discordapp.com/avatars/...",
  "short_description": "...",
  "tags": ["moderation"],
  "server_count": 1234,
  "shard_count": 1,
  "votes_total": 56,
  "votes_month": 7,
  "url": "https://discordbot.fr/bot/BOT_ID"
}
```

- `GET /api/v1/bots/{bot_id}/history?days=90` — historique quotidien, `days` de 1 à 730 (cache 5 min, 120 requêtes par minute et par IP) :

```json
{ "id": "BOT_ID", "days": 90, "points": [{ "day": "2026-01-01", "server_count": 1234, "shard_count": 1, "votes": 3 }] }
```

## Erreurs

Les erreurs renvoient `{ "error": "code" }`.

| HTTP | Code | Signification |
| --- | --- | --- |
| 400 | `invalid_fields` | Champ invalide, listé dans `fields` |
| 401 | `invalid_token` | Jeton absent ou révoqué |
| 403 | `token_mismatch` | Le jeton appartient à un autre bot |
| 404 | `not_found` | Bot inconnu ou non publié |
| 429 | `rate_limited` | Trop de requêtes, réessaie dans une minute |
| 503 | `directory_unavailable` | Annuaire temporairement indisponible |
