> 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/discordpy.md).

# DiscordPY

Dans cet exemple, nous créons 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 appliquez vos propres récompenses** :
   * donner un rôle,
   * ajouter de l’XP,
   * débloquer un salon, etc.

***

#### 1. Pré-requis

* Python **3.10+**
* Un bot Discord fonctionnel (token)
* Un **token API DiscordTop** (`api_token`) associé à votre serveur

Packages à installer :

```bash
pip install -U discord.py aiohttp python-dotenv
```

***

#### 2. Configuration du projet

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

```env
DISCORD_TOKEN=VOTRE_TOKEN_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` correspond à 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. Bot Python complet : commande `/vote` + boutons + appel API DTOP

Créez un fichier `bot.py` avec le contenu suivant :

```python
import os
import asyncio
from dotenv import load_dotenv

import aiohttp
import discord
from discord import app_commands
from discord.ext import commands

load_dotenv()

DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
DISCORD_GUILD_ID = int(os.getenv("DISCORD_GUILD_ID"))
DTOP_API_TOKEN = os.getenv("DTOP_API_TOKEN")
DTOP_GUILD_ID = os.getenv("DTOP_GUILD_ID")  # ID du serveur sur DiscordTop

# Exemple : rôle récompense (à remplacer par un ID réel si vous utilisez cette partie)
REWARD_ROLE_ID = 0  # ex: 123456789012345678

intents = discord.Intents.default()
intents.members = True  # nécessaire si vous attribuez des rôles

bot = commands.Bot(command_prefix="!", intents=intents)


def get_discordtop_vote_url() -> str:
    """Construit l'URL de vote DiscordTop pour votre serveur."""
    return f"https://discordtop.net/guild/{DTOP_GUILD_ID}/vote"


async def check_vote_on_discordtop(user_id: int):
    """
    Appelle l’API DiscordTop pour vérifier si l’utilisateur a voté.

    GET https://api.discordtop.net/v7/check-vote?discord_id=...

    Retourne (status, body_json | None).
    """
    url = "https://api.discordtop.net/v7/vote-check"
    params = {
        "discord_id": str(user_id),
        # "locale": "fr",  # décommentez pour forcer la langue si besoin
    }

    async with aiohttp.ClientSession() as session:
        async with session.get(
            url,
            params=params,
            headers={"Authorization": f"Bearer {DTOP_API_TOKEN}","Accept-Language": "fr-FR"},
        ) as resp:
            status = resp.status
            try:
                data = await resp.json()
            except Exception:
                data = None
            return status, data


class VoteView(discord.ui.View):
    """Vue avec les boutons de vote."""

    def __init__(self):
        super().__init__(timeout=60 * 5)  # 5 minutes
        # Bouton lien vers la page de vote DTOP
        self.add_item(
            discord.ui.Button(
                label="Voter sur DiscordTop",
                style=discord.ButtonStyle.link,
                url=get_discordtop_vote_url(),
            )
        )

    @discord.ui.button(
        label="Vérifier mon vote",
        style=discord.ButtonStyle.primary,
        custom_id="dtop-check-vote",
    )
    async def check_vote_button(
        self,
        interaction: discord.Interaction,
        button: discord.ui.Button,
    ):
        """Callback du bouton 'Vérifier mon vote'."""
        await interaction.response.defer(ephemeral=True)

        user_id = interaction.user.id

        try:
            status, body = await check_vote_on_discordtop(user_id)
        except Exception as exc:
            print("Erreur lors de l'appel à l'API DiscordTop :", exc)
            return await interaction.edit_original_response(
                content="❌ Impossible de contacter l’API DiscordTop pour le moment."
            )

        # Gestion des principaux statuts HTTP
        if status == 200:
        
            if not isinstance(body, dict):
                return await interaction.edit_original_response(
                    content="❌ Réponse inattendue de l’API DiscordTop."
                )
            # Selon la structure de la réponse, vous pouvez vérifier un champ comme `has_voted`

            has_voted = bool(body.get("has_voted", False))
            
            if not has_voted:
                return await interaction.edit_original_response(
                    content=(
                        "❌ Vous n'avez pas encore voté sur DiscordTop !\n"
                        "Cliquez sur « Voter sur DiscordTop », votez, puis réessayez."
                    )
                )
                
            # 👉 C'est ici que vous appliquez VOS récompenses :
            # - ajout de rôle
            # - ajout d'XP
            # - accès à un salon, etc.
            
            if REWARD_ROLE_ID:
                try:
                    member = interaction.user
                    if not isinstance(member, discord.Member):
                        member = await interaction.guild.fetch_member(user_id)

                    role = interaction.guild.get_role(REWARD_ROLE_ID)
                    if role:
                        await member.add_roles(
                            role, reason="Récompense de vote DiscordTop"
                        )
                except Exception as exc:
                    print("Erreur lors de l'attribution du rôle :", exc)

            return await interaction.edit_original_response(
                content="✅ Vote validé sur DiscordTop ! Vos récompenses ont été appliquées."
            )

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

        if status == 429:
            retry_after = 60
            if isinstance(body, dict):
                retry_after = int(body.get("retry_after", retry_after))

            return await interaction.edit_original_response(
                content=(
                    f"🚫 Vous effectuez trop de vérifications de vote.\n"
                    f"Merci de patienter **{retry_after} secondes** avant de réessayer."
                )
            )

        if status in (401, 403):
            return await interaction.edit_original_response(
                content=(
                    "⚠️ La configuration de l'API DiscordTop semble incorrecte "
                    "(clé invalide ou serveur non autorisé). Contactez un administrateur."
                )
            )

        # Autres erreurs (400, 500, etc.)
        print("Erreur API DiscordTop :", status, body)
        return await interaction.edit_original_response(
            content=(
                "❌ Une erreur est survenue lors de la vérification du vote.\n"
                "Merci de réessayer plus tard."
            )
        )


@bot.event
async def on_ready():
    print(f"✅ Connecté en tant que {bot.user} (ID: {bot.user.id})")

    # Synchronisation des commandes slash sur une guilde précise (plus rapide pour les tests)
    guild = discord.Object(id=DISCORD_GUILD_ID)
    bot.tree.copy_global_to(guild=guild)
    await bot.tree.sync(guild=guild)
    print(f"🧵 Commandes synchronisées sur la guilde {DISCORD_GUILD_ID}")


@bot.tree.command(
    name="vote",
    description="Lien de vote DiscordTop + vérification du vote.",
)
async def vote_command(interaction: discord.Interaction):
    """Commande /vote : envoie les boutons."""
    view = VoteView()
    await interaction.response.send_message(
        (
            "Merci de soutenir le serveur en votant sur DiscordTop !\n"
            "Cliquez sur **Voter sur DiscordTop**, puis utilisez **Vérifier mon vote** "
            "pour recevoir vos récompenses."
        ),
        view=view,
        ephemeral=True,
    )


def main():
    bot.run(DISCORD_TOKEN)


if __name__ == "__main__":
    asyncio.run(main())
```

***

#### 4. Où appliquer vos propres récompenses ?

Le bloc à modifier est ici :

```python
if status == 200:
    # 👉 C'est ici que vous appliquez VOS récompenses :
    # - ajout de rôle
    # - ajout d'XP
    # - accès à un salon, etc.
```

À cet endroit, vous pouvez :

* incrémenter un champ XP dans votre base de données,
* donner un rôle temporaire ou permanent,
* ouvrir l’accès à un salon réservé aux voteurs,
* logger l’événement dans un salon staff, etc.

***

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

1. L’utilisateur exécute la commande `/vote`.
2. Le bot envoie un message avec :
   * un bouton **lien** → page de vote DiscordTop,
   * un bouton **« Vérifier mon vote »**.
3. L’utilisateur clique sur **« Vérifier mon vote »**.
4. Le bot appelle :

```http
GET https://api.discordtop.net/v7/check-vote?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/discordpy.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.
