Bot discord w pythonie – discord.py v2 od podstaw #2

Cześć, przedstawię tutaj slash komendy (+ cogi) w discord.py v2

Cogi

Od razu podzielimy nasz kod na pliki, załóżmy, że w każdym osobnym pliku będzie klasa z komendami z danej kategorii.

Najpierw dodajmy do funkcji main, aby ładowała nasze pliki (cogi) do bota. Użyjemy do tego asynchronicznej metody load_extension, która przyjmuje plik do załadowania (bez rozszerzenia, foldery rozdzielamy kropką), więc możemy dla każdego pliku robić

await load_extension('costam')

lecz gdy cogów będzie więcej, to byłoby to trochę bez sensu. Zróbmy więc dla naszych cogów katalog o nazwie np. cogs i tam będziemy umieszczali nasze cogi (w sensie pliki). Dzięki temu możemy użyć funkcji listdir z modułu os, która zwróci nam listę nazw plików z podanego katalogu, musimy z nich uciąć rozszerzenie i dodać „cogs.”, poza tym przyda się sprawdzanie, czy nazwa pliku kończy się rozszerzeniem py, aby w innym wypadku jej nie ładować, ponieważ być może będziemy tam przechowywać jakieś inne pliki wykorzystywane w cogach, więc zróbmy tak

for cog in os.listdir('cogs'):
    if cog.endswith('.py'):
        await bot.load_extension('cogs.' + cog[:-3])

W cogach musimy stworzyć klasę, dziedziczącą po commands.Cog (lub commands.GroupCog jeśli chcemy zrobić grupę komend), która w konstruktorze przyjmuje obiekt clienta (bota) i go ustawia w parametrach obiektu

class OurCog(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

    # i tu definiujemy komendy

Następnie w naszym cogu, musimy stworzyć funkcję setup, którą wywołuje metodę add_cog, powinna ona wyglądać mniej więcej tak

async def setup(bot):
    await bot.add_cog(OurCog(bot))

Slash komendy

Zróbmy więc prosty cog, nazwę go Core, będą w nim najbardziej podstawowe komendy dotyczące samego bota

from discord import app_commands # użyjemy do tworzenia slash komendy
from discord.ext import commands
import discord

class Core(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

async def setup(bot):
    await bot.add_cog(Core(bot))

i tuż pod konstruktorem definiujemy komendy, zwykła slash komenda będzie wyglądać tak

@app_commands.command(name='nazwa_komendy', description='opis komendy', nsfw=False)
async def costam(self, interaction: discord.Interaction):
    # kod naszej komendy
    pass

jeśli nie określimy nazwy komendy, będzie ona taka sama jak nazwa funkcji pod tym dekoratorem, description możemy zapisać jako pomoc do funkcji, z kolei nsfw, gdy ustawione jest na True (a domyślnie jest na False) sprawi, że ta komenda będzie widoczna tylko na kanałach nsfw.

Na każdą interakcję (użycie slash komendy) musimy odpowiedzieć wiadomością lub modalem (jest jeszcze kilka innych możliwości, ale na razie nie będą nam potrzebne). Wszystkie metody do odpowiedzi na interakcje należą do klasy InteractionResponse (interaction.response), zróbmy więc komendę ping, która na naszą wiadomość odpisuje pong

@app_commands.command()
async def ping(self, interaction: discord.Interaction):
    interaction.response.send_message('Pong!')

a teraz zróbmy, żeby wysyłała ping bota (opóźnienie), zwraca to właściwość clienta, latency

@app_commands.command()
async def ping(self, interaction: discord.Interaction):
    interaction.response.send_message('Pong! ' + self.bot.latency)

Slash komendy mają oczywiście opcje (tudzież argumenty czy parametry), dodajemy je po prostu jako argumenty naszej funkcji z typem, np.

@app_commands.command(name='nazwa_komendy', description='opis komendy', nsfw=False)
async def costam(self, interaction: discord.Interaction, costam: str):
    # kod naszej komendy
    pass

lub jeśli chcemy, aby argument był opcjonalny, to przypisujemy mu domyślną wartość tak jak zwykłemu argumentowi funkcji, np.

@app_commands.command(name='nazwa_komendy', description='opis komendy', nsfw=False)
async def costam(self, interaction: discord.Interaction, costam: str = 'Wzium'):
    # kod naszej komendy
    pass

teraz, jeśli użytkownik nie poda costam to w costam bedzie „Wzium”

Spróbuj teraz zrobić kod, który przyjmie od użytkownika imię. Jeśli ostatnia litera imienia to „a”, to wysyła informacje, że jest kobietą, a jeśli ostatnia litera jest inna, to wysyła, że jest mężczyzną. Gdy skończysz, sprawdź, czy twoja komenda działa, jeśli nie to tu jest rozwiązanie

Jeśli chcemy, aby nasza odpowiedź wiadomość odpowiedzi była tymczasowa, widoczna tylko dla autora interakcji (danego użytkownika), wystarczy ustawić ephemeral na True

await interaction.response.send_message('Coś tam!', ephemeral=True)

Argumentami naszej komendy mogą być też listy, z których użytkownik może coś wybrać, lub dynamiczne podpowiadanie tego co chce się wpisać (auto uzupełnienie)

Lista (wybory)

@app_commands.choices(jakis_tam_argument=[
    app_commands.Choice(name='cos', value='cos'),
    app_commands.Choice(name='cos innego', value='cos innego')
])

Autouzupełnienie

@app_commands.autocomplete(cog=jakas_funkcja)
...
async def jakas_funkcja(
    self,
    interaction: discord.Interaction,
    current: str
):
    return [
        app_commands.Choice('costam', value='costam'),
        app_commands.Choice('costam2', value='costam2')
    ]

Argumenty możemy też opisywać…

@app_commands.describe(jakis_argument='opis')

Synchronizacja

Slash komendy muszą być synchronizowane po ich dodaniu lub aktualizacji sygnatury (np. zmiana opcji, opisów). Dzięki temu, że korzystamy z commands.Bot, a nie z discord.Client, nie musimy sami implementować synchronizacji komend, wystarczy, że wywołamy metodę sync klasy app_commands.CommandTree, którą zwraca właściwość tree (klasy clienta). Zróbmy więc komendę (nie slash, zwykłą) do synchronizowania slash komend

@commands.command('sync')
async def sync(ctx: commands.Context):
    if ctx.author.id != 123:
        return await ctx.reply('Nie jesteś właścicielem!')
    await bot.tree.sync()
    await ctx.reply('Zsynchronizowane!')

(oczywiście 123 zamieniamy na nasze id)

W następnym poście będzie kontynuacja slash komend (embedy, buttony)

Chcesz być na bieżąco? Obserwuj nas w google news lub polub nas na Facebooku.
Losowy artykuł
Jak odzyskać hasło FTP z Total Commander?