First pass at documentation reform.

This commit is contained in:
Rapptz
2017-05-12 20:14:34 -04:00
parent be2e706b2a
commit b44bba6ee6
35 changed files with 2558 additions and 682 deletions

View File

@ -16,7 +16,7 @@ Coroutines
Questions regarding coroutines and asyncio belong here.
I get a SyntaxError around the word ``async``\! What should I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This ``SyntaxError`` happens because you're using a Python version lower than 3.5. Python 3.4 uses ``@asyncio.coroutine`` and
``yield from`` instead of ``async def`` and ``await``.
@ -52,7 +52,7 @@ 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
@ -78,13 +78,14 @@ Consider the following example: ::
r = requests.get('http://random.cat/meow')
if r.status_code == 200:
js = r.json()
await client.send_message(channel, js['file'])
await channel.send(js['file'])
# good
async with aiohttp.get('http://random.cat/meow') as r:
if r.status == 200:
js = await r.json()
await client.send_message(channel, js['file'])
async with aiohttp.ClientSession() as session:
async with session.get('http://random.cat/meow') as r:
if r.status == 200:
js = await r.json()
await channel.send(js['file'])
General
---------
@ -103,37 +104,41 @@ following: ::
How do I send a message to a specific channel?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you have its ID then you can do this in two ways, first is by using :class:`Object`\: ::
You must fetch the channel directly and then call the appropriate method. Example: ::
await client.send_message(discord.Object(id='12324234183172'), 'hello')
The second way is by calling :meth:`Client.get_channel` directly: ::
await client.send_message(client.get_channel('12324234183172'), 'hello')
I'm passing IDs as integers and things are not working!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the library IDs must be of type ``str`` not of type ``int``. Wrap it in quotes.
channel = client.get_channel('12324234183172')
await channel.send('hello')
How do I upload an image?
~~~~~~~~~~~~~~~~~~~~~~~~~~
There are two ways of doing it. Both of which involve using :meth:`Client.send_file`.
To upload something to Discord you have to use the :class:`File` object.
The first is by opening the file and passing it directly: ::
A :class:`File` accepts two parameters, the file-like object (or file path) and the filename
to pass to Discord when uploading.
with open('my_image.png', 'rb') as f:
await client.send_file(channel, f)
If you want to upload an image it's as simple as: ::
The second is by passing the file name directly: ::
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)
await client.send_file(channel, 'my_image.png')
How can I add a reaction to a message?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You use the :meth:`Client.add_reaction` method.
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:
@ -141,16 +146,32 @@ If you want to use unicode emoji, you must pass a valid unicode code point in a
- ``'\U0001F44D'``
- ``'\N{THUMBS UP SIGN}'``
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.
Quick example: ::
For custom emoji, you should pass an instance of :class:`discord.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_all_emojis`/:attr:`Server.emojis` to find the one you're looking for.
await message.add_reaction('\N{THUMBS UP SIGN}')
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.
Quick example: ::
# 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)
How do I pass a coroutine to the player's "after" function?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A StreamPlayer is just a ``threading.Thread`` object that plays music. As a result it does not execute inside a coroutine.
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.
@ -169,7 +190,7 @@ However, this function returns a ``concurrent.Future`` and to actually call it w
this together we can do the following: ::
def my_after():
coro = client.send_message(some_channel, 'Song is done!')
coro = some_channel.send('Song is done!')
fut = asyncio.run_coroutine_threadsafe(coro, client.loop)
try:
fut.result()
@ -177,48 +198,44 @@ this together we can do the following: ::
# an error happened sending the message
pass
player = await voice.create_ytdl_player(url, after=my_after)
player.start()
Why is my "after" function being called right away?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``after`` keyword argument expects a *function object* to be passed in. Similar to how ``threading.Thread`` expects a
callable in its ``target`` keyword argument. This means that the following are invalid:
.. code-block:: python
player = await voice.create_ytdl_player(url, after=self.foo())
other = await voice.create_ytdl_player(url, after=self.bar(10))
However the following are correct:
.. code-block:: python
player = await voice.create_ytdl_player(url, after=self.foo)
other = await voice.create_ytdl_player(url, after=lambda: self.bar(10))
Basically, these functions should not be called.
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>`_
`Check the background_task.py example. <https://github.com/Rapptz/discord.py/blob/rewrite/examples/background_task.py>`_
How do I get a specific User/Role/Channel/Server?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How do I get a specific model?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are multiple ways of doing this. If you have a specific entity's ID then you can use
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_server`
- :meth:`Server.get_member`
- :meth:`Server.get_channel`
- :meth:`Client.get_guild`
- :meth:`Client.get_user`
- :meth:`Client.get_emoji`
- :meth:`Guild.get_member`
- :meth:`Guild.get_channel`
The following use an HTTP request:
- :meth:`abc.Messageable.get_message`
- :meth:`Client.get_user_info`
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 entities. The documentation for those functions provide specific examples.
specific models.
Quick example: ::
# 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')
Commands Extension
-------------------
@ -229,10 +246,10 @@ Is there any documentation for this?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Not at the moment. Writing documentation for stuff takes time. A lot of people get by reading the docstrings in the source
code. Others get by via asking questions in the `Discord server <https://discord.gg/0SBTUU1wZTXZNJPa>`_. Others look at the
code. Others get by via asking questions in the `Discord server <https://discord.gg/discord-api>`_. Others look at the
source code of `other existing bots <https://github.com/Rapptz/RoboDanny>`_.
There is a `basic example <https://github.com/Rapptz/discord.py/blob/master/examples/basic_bot.py>`_ showcasing some
There is a `basic example <https://github.com/Rapptz/discord.py/blob/rewrite/examples/basic_bot.py>`_ showcasing some
functionality.
**Documentation is being worked on, it will just take some time to polish it**.
@ -249,42 +266,36 @@ Overriding the default provided ``on_message`` forbids any extra commands from r
await bot.process_commands(message)
Can I use ``bot.say`` in other places aside from commands?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
No. They only work inside commands due to the way the magic involved works.
Why do my arguments require quotes?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In a simple command defined as: ::
@bot.command()
async def echo(message: str):
await bot.say(message)
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: ::
@bot.command()
async def echo(*, message: str):
await bot.say(message)
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``\?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ask the command to pass you the invocation context via ``pass_context``. This context will be passed as the first parameter.
The :class:`~ext.commands.Context` contains an attribute, :attr:`~ext.commands.Context.message` to get the original
message.
Example: ::
@bot.command(pass_context=True)
@bot.command()
async def joined_at(ctx, member: discord.Member = None):
if member is None:
member = ctx.message.author
await bot.say('{0} joined at {0.joined_at}'.format(member))
member = member or ctx.author
await ctx.send('{0} joined at {0.joined_at}'.format(member))
How do I make a subcommand?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -294,15 +305,14 @@ the group operating as "subcommands". These groups can be arbitrarily nested as
Example: ::
@bot.group(pass_context=True)
@bot.group()
async def git(ctx):
if ctx.invoked_subcommand is None:
await bot.say('Invalid git command passed...')
@git.command()
async def push(remote: str, branch: str):
await bot.say('Pushing to {} {}'.format(remote, branch))
async def push(ctx, remote: str, branch: str):
await ctx.send('Pushing to {} {}'.format(remote, branch))
This could then be used as ``?git push origin master``.