Pertanyaan Yang Sering Diajukan

Di halaman ini anda dapat melihat berbagai macam pertanyaan seputar ndiscord.py dan semua ekstensinya. Jangan ragu untuk membuat saran baru atau membuat pull request.

Coroutines

Pertanyaan tentang coroutines dan asyncio akan dijelaskan di sini.

Apa itu coroutine?

coroutine merupakan fungsi yang harus dipakai dengan kata await atau yield from. Ketika Python menemukan await eksekusi fungsi tersebut akan dihentikan sementara dan melakukan hal lain dan kembali ke fungsi tersebut dan membereskan tugasnya. Ini memungkinkan program anda untuk melakukan berbagai hal secara bersamaan tanpa menggunakan threads atau multiprocessing.

Jika anda melupakan await, maka coroutine tidak akan jalan. Jangan lupakan await untuk coroutine.

Di mana saya bisa menggunakan await?

Anda hanya dapat menggunakan await di dalam fungsi async def saja.

Maksudnya "blocking" itu apa?

Di asynchronous programming, panggilan blocking intinya adalah semua bagian fungsi yang tidak await. Jangan hilang harapan dulu, soalnya tidak semua blocking itu buruk! Menggunakan blocking itu tidak dapat dihindari, tapi anda harus dapat membuat fungsi itu tidak terlalu ngeblokir fungsi lain. Ingat, jika anda ngeblock terlalu lama bot anda akan berhenti berfungsi dikarenakan belum selesai mengeksekusi fungsi yang anda gunakan dan tidak bisa melakukan hal lain.

Jika logging diaktifkan, modul ini akan mengingatkan anda kalau sedang terjadi blocking dengan pesan: Heartbeat blocked for more than N seconds. Lihat Menyiapkan Logging untuk info lebih lanjut.

Salah satu contoh blocking yang terlalu lama adalah time.sleep(). Jangan lakukan itu. Gunakan asyncio.sleep(). Kurang lebih seperti ini:

# bad
time.sleep(10)

# good
await asyncio.sleep(10)

Salah satu contoh blocking lain adalah HTTP requests yang terlalu lama dengan modul Requests: HTTP for Humans™. Walaupun modul Requests: HTTP for Humans™ itu bagus untuk modul non-asynchronous, modul tersebut tidak cocok untuk asyncio karena beberapa request akan ngeblock event loop terlalu lama. Jadi, gunakan modul aiohttp yang harusnya terinstall bersama dengan modul ndiscord.py.

Perhatikan contoh berikut:

# bad
r = requests.get('http://aws.random.cat/meow')
if r.status_code == 200:
    js = r.json()
    await channel.send(js['file'])

# good
async with aiohttp.ClientSession() as session:
    async with session.get('http://aws.random.cat/meow') as r:
        if r.status == 200:
            js = await r.json()
            await channel.send(js['file'])

Pertanyaan Umum

Pertanyaan umum tentang penggunaan modul akan dijelaskan di sini.

Di mana saya bisa menemukan contoh penggunaan?

Contoh dapat anda temukan di folder examples di repository.

Bagaimana saya mengatur status "Playing"?

Argumen activity dapat anda berikan ke Client atau Client.change_presence(), dengan memberikan obyek Activity.

Client hanya dapat digunakan pas awal-awal, sementara Client.change_presence() dapat digunakan saat bot sudah aktif.

Peringatan

Tidak disarankan untuk menggunakan Client.change_presence() atau memanggil API lain di on_ready() dikarenakan event ini dapat dipanggil berkali-kali saat bot aktif, bukan sekali saja.

Ada kemungkinan tinggi bot anda akan terputus dari Discord jika presence diubah saat bot sedang menyala.

Tipe status (playing, listening, streaming, watching) dapat diatur dengan enum ActivityType. Untuk optimisasi memori, beberapa aktivitas dibuat versi simpelnya:

Menggunakan keduanya bersama, anda akan melihat hal seperti ini:

client = discord.Client(activity=discord.Game(name='my game'))

# or, for watching:
activity = discord.Activity(name='my activity', type=discord.ActivityType.watching)
client = discord.Client(activity=activity)

Bagaimana saya mengirim pesan ke kanal tertentu?

Anda harus mengambil kanalnya terlebih dahulu dengan metode yang benar. Contoh:

channel = client.get_channel(12324234183172)
await channel.send('hello')

Bagaimana saya mengirim DM?

Gunakan obyek User atau Member dan panggil abc.Messageable.send(). Contohnya:

user = client.get_user(381870129706958858)
await user.send('👀')

Jika anda merespon sebuah event, seperti on_message(), anda harusnya mendapatkan obyek User melalui Message.author:

await message.author.send('👋')

Bagaimana saya mendapatkan ID pesan?

abc.Messageable.send() memberikan obyek Message saat pesan terkirim. ID pesan dapat diakses dengan Message.id:

message = await channel.send('hmm…')
message_id = message.id

Bagaimana saya mengunggah gambar?

Untuk mengunggah sesuatu ke Discord, anda harus menggunakan obyek File.

Obyek File menerima 2 parameter, obyek file-like (atau path file) dan nama file untuk diberikan ke Discord saat diunggah.

Jika anda ingin mengunggah gambar, contohnya seperti ini:

await channel.send(file=discord.File('my_file.png'))

Jika anda mempunyai obyek file-like, anda dapat mengikuti ini:

with open('my_file.png', 'rb') as fp:
    await channel.send(file=discord.File(fp, 'new_filename.png'))

Untuk mengunggah file lebih dari satu, anda dapat menggunakan parameter files daripada file:

my_files = [
    discord.File('result.zip'),
    discord.File('teaser_graph.png'),
]
await channel.send(files=my_files)

Jika anda ingin mengunggah sesuatu dari URL, anda harus menggunakan HTTP request dengan modul aiohttp dan berikan obyek io.BytesIO ke obyek File seperti:

import io
import aiohttp

async with aiohttp.ClientSession() as session:
    async with session.get(my_url) as resp:
        if resp.status != 200:
            return await channel.send('Could not download file...')
        data = io.BytesIO(await resp.read())
        await channel.send(file=discord.File(data, 'cool_image.png'))

Bagaimana saya menambah reaksi ke sebuah pesan?

Anda dapat menggunakan metode Message.add_reaction().

Jika anda ingin menggunakan emoji, anda harus memberikan emoji code point sebagai string. Anda dapat menulis kodenya dengan berbagai cara:

  • '👍'

  • '\U0001F44D'

  • '\N{THUMBS UP SIGN}'

Contoh:

emoji = '\N{THUMBS UP SIGN}'
# or '\U0001f44d' or '👍'
await message.add_reaction(emoji)

Jika anda ingin menggunakan emoji dari pesan, anda sudah mendapatkan code point di bagian konten tanpa harus melakukan hal macam-macam. Anda tidak dapat menggunakan :thumbsup:.

Untuk emoji kustom, anda harus menggunakan obyek Emoji. Anda juga dapat menggunakan string <:name:id>, tapi jika anda dapat mengapatkan emoji yang diinginkan, anda harusnya dapat menggunakan Client.get_emoji() untuk mendapatkan emoji melalui ID atau gunakan metode utils.find()/ utils.get() pada Client.emojis atau Guild.emojis.

Nama dan ID emoji kustom dapat ditemukan di klien Discord dengan menambah backslash ke teks :custom_emoji:. Contohnya, mengirim pesan \:python3: akan mengubahnya menjadi <:python3:232720527448342530>.

Contoh:

# if you have the ID already
emoji = client.get_emoji(310177266011340803)
await message.add_reaction(emoji)

# no ID, do a lookup
emoji = discord.utils.get(guild.emojis, name='LUL')
if emoji:
    await message.add_reaction(emoji)

# if you have the name and ID of a custom emoji:
emoji = '<:python3:232720527448342530>'
await message.add_reaction(emoji)

Bagaimana saya memberikan coroutine setelah fungsi "after"?

Modul penyetel musik jalan di thread lain, modulnya tidak jalan di dalam coroutine. Ini bukan berarti tidak mungkin untuk memberikan coroutine ke parameter after. Untuk melakukan hal tersebut anda harus memberikan sebuah fungsi dan "membungkusnya" dengan beberapa aspek.

Hal pertama yang harus anda perhatikan adalah, coroutine tidak "thread-safe". Secara teknis modul voice berjalan di thread lain, jadi kita harus berhati-hati memanggil fungsi thread-safe agar tidak terjadi kesalahan. Untungnya, asyncio memiliki fungsi asyncio.run_coroutine_threadsafe() yang dapat kita gunakan untuk memanggil coroutine di thread lain.

Tetapi, fungsi ini mengembalikan obyek Future dan untuk memanggilnya kita harus mengambil hasilnya terlebih dahulu, Setelah menggabungkan semuanya menjadi satu, kita dapat melakukan ini:

def my_after(error):
    coro = some_channel.send('Song is done!')
    fut = asyncio.run_coroutine_threadsafe(coro, client.loop)
    try:
        fut.result()
    except:
        # an error happened sending the message
        pass

voice.play(discord.FFmpegPCMAudio(url), after=my_after)

Bagaimana saya menjalankan sesuatu di background?

Coba periksa `contoh background_task.py <https://github.com/naoTimesdev/ndiscord.py/blob/master/examples/background_task.py>_

Bagaimana saya mengambil obyek tertentu?

Ada berbagai cara untuk melakukannya. Jika anda memiliki ID obyek tertentu, anda dapat menggunakan fungsi ini:

Untuk yang ini menggunakan HTTP request:

Jika fungsi diatas tidak dapat membantu, anda dapat mencoba fungsi utils.find() atau utils.get() yang mungkin dapat membantu mencari kelas tertentu.

Contoh:

# find a guild by name
guild = discord.utils.get(client.guilds, name='My Server')

# make sure to check if it's found
if guild is not None:
    # find a channel by name
    channel = discord.utils.get(guild.text_channels, name='cool-channel')

Bagaimana saya melakukan web request?

Untuk melakukan request, disarankan menggunakan modul non-blocking. Modul ini sudah menggunakan modul 3rd-party untuk melakukan requests, aiohttp.

Contoh:

async with aiohttp.ClientSession() as session:
    async with session.get('http://aws.random.cat/meow') as r:
        if r.status == 200:
            js = await r.json()

Mohon kunjungi dokumentasi penuh aiohttp untuk info lebih lanjut.

Bagaimana saya menggunakan gambar lokal untuk dimasukan ke embed?

Discord memiliki cara khusus untuk mengunggah gambar dan menggunkannya di dalam embed agar tidak ditampilkan terpisah, tetapi di dalam embed thumbnail, image, footer, atau author icon.

Untuk melakukannya, unggah gambar seperti biasa dengan abc.Messageable.send(), dan atur URL gambar di embed ke attachment://image.png, di mana image.png merupakan nama file yang anda kirim.

Contoh:

file = discord.File("path/to/my/image.png", filename="image.png")
embed = discord.Embed()
embed.set_image(url="attachment://image.png")
await channel.send(file=file, embed=embed)

Catatan

Dikarenakan limitasi Discord, nama file tidak dapat mengandung garis bawah.

Apakah ada event untuk entri audit log baru?

Dikarenakan Discord tidak memberikan informasi ini lewat gateway mereka, modul ini tidak dapat memberikan informasi ini. Untuk sekarang ini adalah limitasi Discord.

Ekstensi Perintah

Pertanyaan tentang discord.ext.commands akan dijelaskan di sini.

Kenapa on_message membuat semua perintah saya berhenti bekerja?

Melakukan override fungsi default on_message berarti membuat bot tidak dapat menjalankan perintah lain yang ada didalamnya. Untuk memperbaiki ini, mohon tambahkan bot.process_commands(message) diakhir fungsi on_message anda. Sebagai contoh:

@bot.event
async def on_message(message):
    # do some extra stuff here

    await bot.process_commands(message)

Cara lainnya, anda dapat menaruh on_message menjadi listener. Jika anda melakukan ini, anda tidak perlu memanggil fungsi bot.process_commands(). Ini juga memungkinkan anda untuk melakukan berbagai hal dengan asynchronously untuk menjawab semua pesan. Contoh:

@bot.listen('on_message')
async def whatever_you_want_to_call_it(message):
    # do stuff here
    # do not process commands here

Kenapa argument saya membutuhkan kutip?

Sebuah perintah simpel yang didefinisasikan seperti ini:

@bot.command()
async def echo(ctx, message: str):
    await ctx.send(message)

Memanggilnya dengan cara ?echo a b c hanya akan memakai argumen pertama dan membuang argumen lainnya. Untuk memperbaikinya anda harus memanggilnya dengan cara ?echo "a b c" atau mengganti signature perintah untuk memproses hal lainnya. Contoh:

@bot.command()
async def echo(ctx, *, message: str):
    await ctx.send(message)

Ini akan membuat anda dapat menggunakan ?echo a b c tanpa membutuhkan kutip.

Bagaimana saya mendapatkan pesan asli?

Kelas Context terdapat attribut, message untuk mengambil pesan asli.

Contoh:

@bot.command()
async def length(ctx):
    await ctx.send(f'Your message is {len(ctx.message.content)} characters long.')

Bagaimana saya membuat subperintah?

Gunakan decorator group(). Ini akan mengubah fungsi callback menjadi Group yang akan membuat anda bisa menambah perintah ke sebuah grup, membuatnya sebagai "subcommands".

Contoh:

@bot.group()
async def git(ctx):
    if ctx.invoked_subcommand is None:
        await ctx.send('Invalid git command passed...')

@git.command()
async def push(ctx, remote: str, branch: str):
    await ctx.send(f'Pushing to {remote} {branch}')

Ini dapat digunakan seperti ?git push origin master.