File: faq.rst

package info (click to toggle)
python-discord 2.5.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,180 kB
  • sloc: python: 46,013; javascript: 363; makefile: 154
file content (502 lines) | stat: -rw-r--r-- 18,009 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
:orphan:

.. currentmodule:: discord
.. _faq:

Frequently Asked Questions
===========================

This is a list of Frequently Asked Questions regarding using ``discord.py`` and its extension modules. Feel free to suggest a
new question or submit one via pull requests.

.. contents:: Questions
    :local:

Coroutines
------------

Questions regarding coroutines and asyncio belong here.

What is a coroutine?
~~~~~~~~~~~~~~~~~~~~~~

A |coroutine_link|_ is a function that must be invoked with ``await`` or ``yield from``. When Python encounters an ``await`` it stops
the function's execution at that point and works on other things until it comes back to that point and finishes off its work.
This allows for your program to be doing multiple things at the same time without using threads or complicated
multiprocessing.

**If you forget to await a coroutine then the coroutine will not run. Never forget to await a coroutine.**

Where can I use ``await``\?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can only use ``await`` inside ``async def`` functions and nowhere else.

What does "blocking" mean?
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In asynchronous programming a blocking call is essentially all the parts of the function that are not ``await``. Do not
despair however, because not all forms of blocking are bad! Using blocking calls is inevitable, but you must work to make
sure that you don't excessively block functions. Remember, if you block for too long then your bot will freeze since it has
not stopped the function's execution at that point to do other things.

If logging is enabled, this library will attempt to warn you that blocking is occurring with the message:
``Heartbeat blocked for more than N seconds.``
See :ref:`logging_setup` for details on enabling logging.

A common source of blocking for too long is something like :func:`time.sleep`. Don't do that. Use :func:`asyncio.sleep`
instead. Similar to this example: ::

    # bad
    time.sleep(10)

    # good
    await asyncio.sleep(10)

Another common source of blocking for too long is using HTTP requests with the famous module :doc:`req:index`.
While :doc:`req:index` is an amazing module for non-asynchronous programming, it is not a good choice for
:mod:`asyncio` because certain requests can block the event loop too long. Instead, use the :doc:`aiohttp <aio:index>` library which
is installed on the side with this library.

Consider the following example: ::

    # 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'])

General
---------

General questions regarding library usage belong here.

Where can I find usage examples?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example code can be found in the `examples folder <https://github.com/Rapptz/discord.py/tree/master/examples>`_
in the repository.

How do I set the "Playing" status?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``activity`` keyword argument may be passed in the :class:`Client` constructor or :meth:`Client.change_presence`, given an :class:`Activity` object.

The constructor may be used for static activities, while :meth:`Client.change_presence` may be used to update the activity at runtime.

.. warning::

    It is highly discouraged to use :meth:`Client.change_presence` or API calls in :func:`on_ready` as this event may be called many times while running, not just once.

    There is a high chance of disconnecting if presences are changed right after connecting.

The status type (playing, listening, streaming, watching) can be set using the :class:`ActivityType` enum.
For memory optimisation purposes, some activities are offered in slimmed-down versions:

- :class:`Game`
- :class:`Streaming`

Putting both of these pieces of info together, you get the following: ::

    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)

How do I send a message to a specific channel?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You must fetch the channel directly and then call the appropriate method. Example: ::

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

How do I send a DM?
~~~~~~~~~~~~~~~~~~~

Get the :class:`User` or :class:`Member` object and call :meth:`abc.Messageable.send`. For example: ::

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

If you are responding to an event, such as :func:`on_message`, you already have the :class:`User` object via :attr:`Message.author`: ::

    await message.author.send('👋')

How do I get the ID of a sent message?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:meth:`abc.Messageable.send` returns the :class:`Message` that was sent.
The ID of a message can be accessed via :attr:`Message.id`: ::

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

How do I upload an image?
~~~~~~~~~~~~~~~~~~~~~~~~~

To upload something to Discord you have to use the :class:`File` object.

A :class:`File` accepts two parameters, the file-like object (or file path) and the filename
to pass to Discord when uploading.

If you want to upload an image it's as simple as: ::

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

If you have a file-like object you can do as follows: ::

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

To upload multiple files, you can use the ``files`` keyword argument instead of ``file``\: ::

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

If you want to upload something from a URL, you will have to use an HTTP request using :doc:`aiohttp <aio:index>`
and then pass an :class:`io.BytesIO` instance to :class:`File` like so:

.. code-block:: python3

    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'))


How can I add a reaction to a message?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You use the :meth:`Message.add_reaction` method.

If you want to use unicode emoji, you must pass a valid unicode code point in a string. In your code, you can write this in a few different ways:

- ``'👍'``
- ``'\U0001F44D'``
- ``'\N{THUMBS UP SIGN}'``

Quick example:

.. code-block:: python3

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

In case you want to use emoji that come from a message, you already get their code points in the content without needing
to do anything special. You **cannot** send ``':thumbsup:'`` style shorthands.

For custom emoji, you should pass an instance of :class:`Emoji`. You can also pass a ``'<:name:id>'`` string, but if you
can use said emoji, you should be able to use :meth:`Client.get_emoji` to get an emoji via ID or use :func:`utils.find`/
:func:`utils.get` on :attr:`Client.emojis` or :attr:`Guild.emojis` collections.

The name and ID of a custom emoji can be found with the client by prefixing ``:custom_emoji:`` with a backslash.
For example, sending the message ``\:python3:`` with the client will result in ``<:python3:232720527448342530>``.

Quick example:

.. code-block:: python3


    # 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)

How do I pass a coroutine to the player's "after" function?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The library's music player launches on a separate thread, ergo it does not execute inside a coroutine.
This does not mean that it is not possible to call a coroutine in the ``after`` parameter. To do so you must pass a callable
that wraps up a couple of aspects.

The first gotcha that you must be aware of is that calling a coroutine is not a thread-safe operation. Since we are
technically in another thread, we must take caution in calling thread-safe operations so things do not bug out. Luckily for
us, :mod:`asyncio` comes with a :func:`asyncio.run_coroutine_threadsafe` function that allows us to call
a coroutine from another thread.

However, this function returns a :class:`~concurrent.futures.Future` and to actually call it we have to fetch its result. Putting all of
this together we can do the following:

.. code-block:: python3

    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)

How do I run something in the background?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`Check the background_task.py example. <https://github.com/Rapptz/discord.py/blob/master/examples/background_task.py>`_

How do I get a specific model?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are multiple ways of doing this. If you have a specific model's ID then you can use
one of the following functions:

- :meth:`Client.get_channel`
- :meth:`Client.get_guild`
- :meth:`Client.get_user`
- :meth:`Client.get_emoji`
- :meth:`Guild.get_member`
- :meth:`Guild.get_channel`
- :meth:`Guild.get_role`

The following use an HTTP request:

- :meth:`abc.Messageable.fetch_message`
- :meth:`Client.fetch_user`
- :meth:`Client.fetch_guilds`
- :meth:`Client.fetch_guild`
- :meth:`Guild.fetch_emoji`
- :meth:`Guild.fetch_emojis`
- :meth:`Guild.fetch_member`


If the functions above do not help you, then use of :func:`utils.find` or :func:`utils.get` would serve some use in finding
specific models.

Quick example:

.. code-block:: python3

    # 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')

How do I make a web request?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To make a request, you should use a non-blocking library.
This library already uses and requires a 3rd party library for making requests, :doc:`aiohttp <aio:index>`.

Quick example:

.. code-block:: python3

    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()

See `aiohttp's full documentation <http://aiohttp.readthedocs.io/en/stable/>`_ for more information.

.. _local_image:

How do I use a local image file for an embed image?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Discord special-cases uploading an image attachment and using it within an embed so that it will not
display separately, but instead in the embed's thumbnail, image, footer or author icon.

To do so, upload the image normally with :meth:`abc.Messageable.send`,
and set the embed's image URL to ``attachment://image.png``,
where ``image.png`` is the filename of the image you will send.


Quick example:

.. code-block:: python3

    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)

Is there an event for audit log entries being created?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This event is now available in the library and Discord as of version 2.2. It can be found under :func:`on_audit_log_entry_create`.


Commands Extension
-------------------

Questions regarding ``discord.ext.commands`` belong here.

Why does ``on_message`` make my commands stop working?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Overriding the default provided ``on_message`` forbids any extra commands from running. To fix this, add a
``bot.process_commands(message)`` line at the end of your ``on_message``. For example: ::

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

        await bot.process_commands(message)

Alternatively, you can place your ``on_message`` logic into a **listener**. In this setup, you should not
manually call ``bot.process_commands()``. This also allows you to do multiple things asynchronously in response
to a message. Example::

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

Why do my arguments require quotes?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In a simple command defined as:

.. code-block:: python3

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

Calling it via ``?echo a b c`` will only fetch the first argument and disregard the rest. To fix this you should either call
it via ``?echo "a b c"`` or change the signature to have "consume rest" behaviour. Example:

.. code-block:: python3

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

This will allow you to use ``?echo a b c`` without needing the quotes.

How do I get the original ``message``\?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :class:`~ext.commands.Context` contains an attribute, :attr:`~.Context.message` to get the original
message.

Example:

.. code-block:: python3

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

How do I make a subcommand?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use the :func:`~ext.commands.group` decorator. This will transform the callback into a :class:`~ext.commands.Group` which will allow you to add commands into
the group operating as "subcommands". These groups can be arbitrarily nested as well.

Example:

.. code-block:: python3

    @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}')

This could then be used as ``?git push origin master``.

Views and Modals
-----------------

Questions regarding :class:`discord.ui.View`, :class:`discord.ui.Modal`, and their components such as buttons, select menus, etc.

How can I disable all items on timeout?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This requires three steps.

1. Attach a message to the :class:`~discord.ui.View` using either the return type of :meth:`~abc.Messageable.send` or retrieving it via :attr:`InteractionCallbackResponse.resource`.
2. Inside :meth:`~ui.View.on_timeout`, loop over all items inside the view and mark them disabled.
3. Edit the message we retrieved in step 1 with the newly modified view.

Putting it all together, we can do this in a text command:

.. code-block:: python3

    class MyView(discord.ui.View):
        async def on_timeout(self) -> None:
            # Step 2
            for item in self.children:
                item.disabled = True

            # Step 3
            await self.message.edit(view=self)

        @discord.ui.button(label='Example')
        async def example_button(self, interaction: discord.Interaction, button: discord.ui.Button):
            await interaction.response.send_message('Hello!', ephemeral=True)

    @bot.command()
    async def timeout_example(ctx):
        """An example to showcase disabling buttons on timing out"""
        view = MyView()
        # Step 1
        view.message = await ctx.send('Press me!', view=view)

Application commands, when you respond with :meth:`InteractionResponse.send_message`, return an instance of :class:`InteractionCallbackResponse` which contains the message you sent. This is the message you should attach to the view.

Putting it all together, using the previous view definition:

.. code-block:: python3

    @tree.command()
    async def more_timeout_example(interaction):
        """Another example to showcase disabling buttons on timing out"""
        view = MyView()
        callback = await interaction.response.send_message('Press me!', view=view)

        # Step 1
        resource = callback.resource
        # making sure it's an interaction response message
        if isinstance(resource, discord.InteractionMessage):
            view.message = resource


Application Commands
--------------------

Questions regarding Discord's new application commands, commonly known as "slash commands" or "context menu commands".

My bot's commands are not showing up!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. Did you :meth:`~.CommandTree.sync` your command? Commands need to be synced before they will appear.
2. Did you invite your bot with the correct permissions? Bots need to be invited with the ``applications.commands``
   scope in addition to the ``bot`` scope. For example, invite the bot with the following URL:
   ``https://discord.com/oauth2/authorize?client_id=<client id>&scope=applications.commands+bot``.
   Alternatively, if you use :func:`utils.oauth_url`, you can call the function as such:
   ``oauth_url(<other options>, scopes=("bot", "applications.commands"))``.