よくある質問¶
これは discord.py
及び 拡張モジュールに対して、よくある質問をまとめたものです。気軽に質問やプルリクエストを提出してください。
質問
コルーチン¶
コルーチンとasyncioに関する質問。
コルーチンとはなんですか。¶
coroutine とは await
または yield from
から呼び出さなければならない関数です。 await
にエンカウントした場合、そのポイントで関数の実行を停止し、他の作業を実行します。 これは作業が終了し、このポイントに戻ってくるまで続きます。 これにより、スレッドや複雑なマルチプロセッシングを用いずに複数の処理を並列実行することができます。
コルーチンにawaitを記述し忘れた場合、コルーチンは実行されません。awaitの記述を忘れないように注意してください。
await
はどこで使用することができますか。¶
await
は async def
関数の中でのみ使用できます。
「ブロッキング」とはなんですか。¶
非同期プログラミングにおけるブロッキングとは、関数内の await
修飾子がないコードすべてを指します。 しかし、全てのブロッキングが悪いというわけではありません。ブロッキングを使用することは避けられませんが、ブロックの発生は出来るだけ少なくする必要があります。長時間のブロックが発生すると、関数の実行が停止しないため、長時間Botがフリーズすることになることを覚えておきましょう。
もしロギングを有効にしている場合、ライブラリはブロッキングが起きていることを次のメッセージで警告しようと試みます: Heartbeat blocked for more than N seconds.
ロギングを有効にするには、:ref:`logging_setup`をご覧ください。
長時間ブロックの原因として一般的なのは time.sleep()
などです。 これは使用せず、下記の例のように asyncio.sleep()
を使用してください。
# bad
time.sleep(10)
# good
await asyncio.sleep(10)
また、これだけでなく、有名なモジュール requests のHTTPリクエストも長時間ブロックの原因になります。 requests モジュールは非非同期プログラミングでは素晴らしいモジュールですが、特定のリクエストがイベントループを長時間ブロックする可能性があるため、 asyncio
には適していません。 代わりにこのライブラリと一緒にインストールされた aiohttp を使用してください。
次の例を見てみましょう。
# 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'])
一般¶
ライブラリの使用に関する一般的な質問。
Where can I find usage examples?¶
Example code can be found in the examples folder in the repository.
「プレイ中」状態の設定をするにはどうすればいいですか。¶
The activity
keyword argument may be passed in the Client
constructor or Client.change_presence()
, given an Activity
object.
The constructor may be used for static activities, while Client.change_presence()
may be used to update the activity at runtime.
警告
It is highly discouraged to use Client.change_presence()
or API calls in 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.
ステータスタイプ(プレイ中、再生中、配信中、視聴中)は列挙型の ActivityType
を指定することで設定が可能です。メモリの最適化のため、一部のアクティビティはスリム化したバージョンで提供しています。
これらの情報をまとめると以下のようになります:
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)
特定のチャンネルにメッセージを送るにはどうすればいいですか。¶
チャンネルを直接取得してから、適切なメソッドの呼び出しを行う必要があります。以下がその例です。
channel = client.get_channel(12324234183172)
await channel.send('hello')
How do I send a DM?¶
Get the User
or Member
object and call abc.Messageable.send()
. For example:
user = client.get_user(381870129706958858)
await user.send('👀')
If you are responding to an event, such as on_message()
, you already have the User
object via Message.author
:
await message.author.send('👋')
How do I get the ID of a sent message?¶
abc.Messageable.send()
returns the Message
that was sent.
The ID of a message can be accessed via Message.id
:
message = await channel.send('hmm…')
message_id = message.id
画像をアップロードするにはどうすればいいですか。¶
Discordに何かをアップロードする際には File
オブジェクトを使用する必要があります。
File
は二つのパラメータがあり、ファイルライクなオブジェクト(または、そのファイルパス)と、ファイル名を渡すことができます。
画像をアップロードするだけなら、以下のように簡単に行なえます。
await channel.send(file=discord.File('my_file.png'))
もし、ファイルライクなオブジェクトがあるなら、以下のような実装が可能です。
with open('my_file.png', 'rb') as fp:
await channel.send(file=discord.File(fp, 'new_filename.png'))
複数のファイルをアップロードするには、 file
の代わりに files
を使用しましょう。
my_files = [
discord.File('result.zip'),
discord.File('teaser_graph.png'),
]
await channel.send(files=my_files)
URLから何かをアップロードする場合は、 aiohttp のHTTPリクエストを使用し、 io.BytesIO
インスタンスを File
にわたす必要があります。
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'))
メッセージにリアクションをつけるにはどうすればいいですか。¶
Client.add_reaction()
を使用してください。
Unicodeの絵文字を使用する場合は、文字列内の有効なUnicodeのコードポイントを渡す必要があります。 例を挙げると、このようになります。
'👍'
'\U0001F44D'
'\N{THUMBS UP SIGN}'
簡単な例。
emoji = '\N{THUMBS UP SIGN}'
# or '\U0001f44d' or '👍'
await message.add_reaction(emoji)
メッセージから来た絵文字を使用したい場合は、特になにをするでもなく、コンテンツのコードポイントをあなたは取得しています。また、 ':thumbsup:'
のような簡略化したものを送信することは できません 。
カスタム絵文字については、Emoji`のインスタンスを渡すといいでしょう。`
'<:名前:ID>'``形式の文字列も渡せますが、その絵文字が使えるなら、Client.get_emoji`でIDから絵文字を取得したり、:attr:`Client.emojis()
や Guild.emojis`に対して:func:`utils.find
/ :func:`utils.get`を使ったりできるでしょう。
カスタム絵文字の名前とIDをクライアント側で知るには、:カスタム絵文字:``の頭にバックスラッシュ(円記号)をつけます。たとえば、メッセージ
:python3:``を送信すると、結果は``<:python3:232720527448342530>``になります。
簡単な例。
# 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)
どうやってコルーチンをプレイヤーの後処理に渡すのですか。¶
ライブラリの音楽プレーヤーは別のスレッドで起動するもので、コルーチン内で実行されるものではありません。しかし、 after
にコルーチンが渡せないというわけではありません。コルーチンを渡すためには、いくつかの機能を包括した呼び出し可能コードで渡す必要があります。
コルーチンを呼び出すという動作はスレッドセーフなものではないということを最初に理解しておく必要があります。技術的に別スレッドなので、スレッドセーフに呼び出す際には注意が必要です。幸運にも、 asyncio
には asyncio.run_coroutine_threadsafe()
という関数があります。これを用いることで、別スレッドからコルーチンを呼び出すことが可能です。
However, this function returns a Future
and to actually call it we have to fetch its result. Putting all of
this together we can do the following:
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)
バックグラウンドで何かを動かすにはどうすればいいですか。¶
特定のユーザー、役割、チャンネル、サーバを取得するにはどうすればいいですか。¶
方法は複数ありますが、特定のモデルのIDがわかっていれば、以下の方法が使えます。
以下の例ではHTTPリクエストを使用します。
上記の関数を使えない状況の場合、 utils.find()
または utils.get()
が役に立つでしょう。
簡単な例。
# 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')
Webリクエストはどうやって作ればよいですか。¶
To make a request, you should use a non-blocking library. This library already uses and requires a 3rd party library for making requests, aiohttp.
簡単な例。
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()
詳細は aiohttpの完全なドキュメント を参照してください。
Embedの画像にローカルの画像を使用するにはどうすればいいですか。¶
特殊なケースとして、画像が別々に表示されないようDiscordにembedを用いてアップロードする際、画像は代わりにembedのサムネイルや画像、フッター、製作者アイコンに表示されます。
これを行うには、通常通り abc.Messageable.send()
を用いて画像をアップロードし、Embedの画像URLに attachment://image.png
を設定します。このとき image.png
は送信したい画像のファイル名にです。
簡単な例。
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)
注釈
Discord側の制限により、ファイル名にアンダースコアが含まれていない場合があります。
Is there an event for audit log entries being created?¶
Discordはゲートウェイでこの情報をディスパッチしないため、ライブラリによってこの情報を提供することはできません。これは現在、Discord側の制限です。
コマンド拡張¶
discord.ext.commands
に関する質問。
on_message
を使うとコマンドが動作しなくなります。どうしてですか。¶
デフォルトで提供されている on_message
をオーバーライドすると、コマンドが実行されなくなります。これを修正するには on_message
の最後に bot.process_commands(message)
を追加してみてください。
@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
コマンドの引数にクォーテーションが必要なのはなぜですか。¶
次の簡単なコマンドを見てみましょう。
@bot.command()
async def echo(ctx, message: str):
await ctx.send(message)
このコマンドを ?echo a b c
のように実行したとき、コマンドに渡されるのは最初の引数だけです。その後の引数はすべて無視されます。これを正常に動かすためには ?echo "a b c"
のようにしてコマンドを実行するか、コマンドの引数を下記の例のようにしてみましょう
@bot.command()
async def echo(ctx, *, message: str):
await ctx.send(message)
これにより、クォーテーションなしで ?echo a b c
を使用することができます。
元の message
を取得するにはどうすればよいですか。¶
Context
は元のメッセージを取得するための属性である message
を持っています。
例:
@bot.command()
async def length(ctx):
await ctx.send('Your message is {} characters long.'.format(len(ctx.message.content)))
サブコマンドを作るにはどうすればいいですか。¶
Use the group()
decorator. This will transform the callback into a Group
which will allow you to add commands into
the group operating as "subcommands". These groups can be arbitrarily nested as well.
例:
@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('Pushing to {} {}'.format(remote, branch))
これは ?git push origin master
のように使うことができます。