> For the complete documentation index, see [llms.txt](https://docs.discordtop.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.discordtop.net/documentation/exemples/discordjs.md).

# DiscordJS

Dans cet exemple, on crée une commande `/vote` qui :

1. Envoie un message avec :
   * un bouton **“Voter sur DiscordTop”** → ouvre la page de vote,
   * un bouton **“Vérifier mon vote”** → appelle l’API DiscordTop.
2. Si le vote est validé par l’API, **vous pouvez appliquer vos propres récompenses** :
   * donner un rôle,
   * ajouter de l’XP,
   * ouvrir l’accès à un salon privé, etc.

***

### 1. Pré-requis

* Node.js **18+** (pour avoir `fetch` intégré)
* Un bot Discord configuré (token)
* Un **token API DiscordTop** (`api_token`) associé à votre serveur - [Comment trouvé ma clé ?](/api-reference/concepts-cles/authentification.md)

Packages :

```bash
npm install discord.js dotenv
```

***

### 2. Configuration du projet

Créez un fichier `.env` à la racine :

```env
DISCORD_TOKEN=VOTRE_TOKEN_BOT
DISCORD_CLIENT_ID=ID_CLIENT_DE_VOTRE_BOT
DISCORD_GUILD_ID=ID_DU_SERVEUR_DE_TEST
DTOP_API_TOKEN=VOTRE_CLE_API_DISCORDTOP
DTOP_GUILD_ID=ID_DU_SERVEUR_SUR_DTOP
```

> `DTOP_GUILD_ID` est l’ID du serveur tel qu’il apparaît sur DiscordTop (en général, c’est le même que l’ID Discord).

***

### 3. Enregistrer la commande `/vote`

Créez un fichier `deploy-commands.js` :

{% code overflow="wrap" %}

```js
import 'dotenv/config';
import { REST, Routes, SlashCommandBuilder } from 'discord.js';

const commands = [
  new SlashCommandBuilder()
    .setName('vote')
    .setDescription('Obtenir le lien de vote DiscordTop et vérifier votre vote.')
    .toJSON(),
];

const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);

async function main() {
  try {
    console.log('🔁 Mise à jour des commandes (guild)…');
    await rest.put(
      Routes.applicationGuildCommands(
        process.env.DISCORD_CLIENT_ID,
        process.env.DISCORD_GUILD_ID
      ),
      { body: commands }
    );
    console.log('✅ Commandes enregistrées avec succès.');
  } catch (error) {
    console.error('❌ Erreur lors de l’enregistrement des commandes :', error);
  }
}

main();
```

{% endcode %}

Lancer une fois :

```bash
node deploy-commands.js
```

***

### 4. Bot de base avec `/vote` + boutons

Créez un fichier `index.js` :

```js
import 'dotenv/config';
import {
  Client,
  GatewayIntentBits,
  Partials,
  ButtonStyle,
  ActionRowBuilder,
  ButtonBuilder,
  Events,
} from 'discord.js';

const client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
  partials: [Partials.GuildMember],
});

// URL de vote DTOP (adapter si besoin)
function getDiscordTopVoteUrl() {
  const guildId = process.env.DTOP_GUILD_ID;
  return `https://discordtop.net/guild/${guildId}/vote`;
}

// Appel à l’API DTOP pour vérifier le vote
async function checkVoteOnDiscordTop(userId) {
  const apiToken = process.env.DTOP_API_TOKEN;
  const url = new URL('https://api.discordtop.net/v7/vote-check');
  
  url.searchParams.set('discord_id', userId);

  // Vous pouvez aussi ajouter &locale=fr si vous voulez forcer la langue
  // url.searchParams.set('locale', 'fr');

  const res = await fetch(url.toString(), {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Accept-Language': 'fr-FR',
    },
  });

  // On retourne la réponse brute + le JSON
  let body = null;
  try {
    body = await res.json();
  } catch {
    body = null;
  }

  return { status: res.status, body };
}

client.once(Events.ClientReady, (c) => {
  console.log(`✅ Connecté en tant que ${c.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  // Commande /vote
  if (interaction.isChatInputCommand() && interaction.commandName === 'vote') {
    const voteUrl = getDiscordTopVoteUrl();

    const row = new ActionRowBuilder().addComponents(
      new ButtonBuilder()
        .setLabel('Voter sur DiscordTop')
        .setStyle(ButtonStyle.Link)
        .setURL(voteUrl),
      new ButtonBuilder()
        .setCustomId('dtop-check-vote')
        .setLabel('Vérifier mon vote')
        .setStyle(ButtonStyle.Primary)
    );

    await interaction.reply({
      content:
        'Merci de soutenir le serveur en votant sur DiscordTop !\nCliquez sur le bouton ci-dessous pour voter, puis utilisez **“Vérifier mon vote”** pour recevoir vos récompenses.',
      components: [row],
      ephemeral: true,
    });

    return;
  }

  // Bouton "Vérifier mon vote"
  if (interaction.isButton() && interaction.customId === 'dtop-check-vote') {
    await interaction.deferReply({ ephemeral: true });

    const userId = interaction.user.id;

    try {
      const { status, body } = await checkVoteOnDiscordTop(userId);

      // Gestion des statuts principaux
      if (status === 200) {
        const hasVoted = body?.has_voted ?? false;

        if (!hasVoted) {
          return interaction.editReply(
            "Il semble que vous n'ayez pas encore voté pour ce serveur. Essayez de voter puis réessayez dans quelques secondes."
          );
        }

        // 👉 C’est ici que vous appliquez VOS récompenses :
        // - donner un rôle
        // - ajouter de l’XP
        // - débloquer un salon, etc.

        // Exemple : donner un rôle (remplacez par votre ID de rôle)
        const rewardRoleId = 'ID_DU_ROLE_RECOMPENSE'; // à adapter

        const member =
          interaction.member ??
          (await interaction.guild.members.fetch(userId).catch(() => null));

        if (member && rewardRoleId !== 'ID_DU_ROLE_RECOMPENSE') {
          await member.roles.add(rewardRoleId).catch(() => null);
        }

        return interaction.editReply(
          "✅ Vote validé sur DiscordTop ! Vos récompenses ont été appliquées sur le serveur."
        );
      }

      if (status === 404) {
        return interaction.editReply(
          "Aucun vote récent n'a été trouvé pour votre compte. Assurez-vous d'avoir voté sur la bonne page et réessayez dans quelques instants."
        );
      }

      if (status === 429) {
        const retryAfter = body?.retry_after ?? 60;
        return interaction.editReply(
          `🚫 Vous effectuez trop de vérifications de vote. Merci de patienter **${retryAfter} secondes** avant de réessayer.`
        );
      }

      if (status === 401 || status === 403) {
        return interaction.editReply(
          "⚠️ La configuration de l'API DiscordTop semble incorrecte (clé invalide ou serveur non autorisé). Contactez un administrateur."
        );
      }

      // Autres erreurs (500, 400, etc.)
      console.error('Erreur API DTOP:', status, body);
      return interaction.editReply(
        "❌ Une erreur est survenue lors de la vérification du vote. Merci de réessayer plus tard."
      );
    } catch (err) {
      console.error('Erreur lors de l’appel à DTOP :', err);
      return interaction.editReply(
        "❌ Impossible de contacter l’API DiscordTop pour le moment."
      );
    }
  }
});

client.login(process.env.DISCORD_TOKEN);
```

***

### 5. Où brancher votre propre logique de récompenses ?

Dans l’exemple ci-dessus, le bloc important est ici :

```js
if (status === 200) {
  const hasVoted = body?.has_voted ?? true;

  if (!hasVoted) {
    return interaction.editReply(
      "Il semble que vous n'ayez pas encore voté pour ce serveur. Essayez de voter puis réessayez dans quelques secondes."
    );
  }

  // 👉 VOTRE LOGIQUE DE RÉCOMPENSE
  // Exemple : donner un rôle, XP, etc.
}
```

C’est à cet endroit précis que vous pouvez :

* incrémenter un champ XP dans votre base,
* ajouter un rôle avec `member.roles.add(...)`,
* logger l’événement dans un salon staff,
* comptabiliser les votes journaliers, etc.

***

### 6. Résumé du flux côté bot

1. L’utilisateur tape `/vote`
2. Le bot répond avec :
   * un bouton **lien** → page de vote DiscordTop,
   * un bouton **“Vérifier mon vote”**
3. L’utilisateur clique “Vérifier mon vote”
4. Le bot appelle :

```http
GET https://api.discordtop.net/v7/vote-check?api_token=VOTRE_CLE_API&discord_id=USER_ID
```

5. Selon la réponse :
   * ✅ 200 → vote valide → vos récompenses sont appliquées
   * ❌ 404 → pas de vote récent
   * 🚫 429 → trop de requêtes, respectez `retry_after`
   * 🔐 401/403 → problème de configuration API
   * 💥 500 → erreur côté DTOP (à réessayer plus tard)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.discordtop.net/documentation/exemples/discordjs.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
