Help us understand the problem. What is going on with this article?

Discord.py botが動かない場合に確かめる事

Discord公式APIのラッパー、Discord.pyを使ってBOTを作成し始めた人がよく突き当たる問題とその解決法をまとめました。

(環境構築ではなく、実際に動作させる場合に関する問題についての記事です)

もし新たにエラーや原因不明の挙動を発見したら追記します。

環境・対象

  • Discord.py ver1.0.0 以上
  • Python ver3.6 以上
  • Windows10 (筆者の動作環境)

対象はDiscord.pyでbotを作り始めたもののエラーの意味がわからない、エラーは消えたけど動かない、という人です。


これを読む前に、

  1. エラー文で検索する
  2. 起こった事象を単語で検索する
  3. ドキュメントのよくある質問を読む

何度検索しても、この記事を読んでも解決しなければ以下のサーバーに質問するのが良いです。
少し高度な問題にぶつかっている可能性があります。

一時的なエラー

ここに列挙するのはアップデートやDiscordの設定変更で改善される可能性の高い、突発的なエラー群です。


pipを使用する場合は以下のようにアップデートできます。

python3 -m pip install -U discord.py

Windowsの場合は以下のようにすると上手く行くかもしれません。

py -3 -m pip install -U discord.py

socket.gaierror: [Errno 11001] getaddrinfo failed

discord.VoiceChannel.connect() 使用時に発生するエラーです。
これは、サーバー地域が「日本」に設定されていることが原因のようなので、一時的に別の地域に変更してみてください。

以下のようなエラーです:

Task exception was never retrieved
future: <Task finished name='Task-23' coro=<VoiceClient._create_socket() done, defined at Python38\lib\site-packages\discord\voice_client.py:172> exception=gaierror(11001, 'getaddrinfo failed')>
Traceback (most recent call last):
  File "Python38\lib\site-packages\discord\voice_client.py", line 191, in _create_socket
    self.endpoint_ip = socket.gethostbyname(self.endpoint)
socket.gaierror: [Errno 11001] getaddrinfo failed

TypeError: new() got an unexpected keyword argument 'allow_new'

権限操作を行う際に発生するエラーのようです。
直近のDiscord.pyのアップデートで修正されています。

TypeError: new() got an unexpected keyword argument 'deny_new'

起動時に発生するエラーのようです。
最新のDiscord.pyをインストールして対応してください。

KeyError: 'animated'

リアクションに関連する操作を行った際、API側で animated が削除されていたことに起因するものでした。
Discord.py v1.2.5で修正されています。

よくあるエラー

IndexError: list index out of range

リストに存在しない番号の要素を参照しようとしています。
print(リスト名)で、リストの状況を順番に確認しましょう。


UnboundLocalError: local variable '変数名' referenced before assignment

まだその時点で定義されていないローカル変数を使用しています。
変数の定義の位置をもう一度確かめましょう。

もしかするとその行の実行時には利用できないような位置で定義している可能性があります。


TypeError: 関数名() missing n required positional argument: '引数名'

関数の引数が足りていません。
Discord側のイベントであればドキュメントを見直しましょう。

例えば、on_message関数はmessage引数を受け取るのに、

@client.event
async def on_message(): # 引数が無い!
    print("Hello")

TypeError: 関数名() takes n positional arguments but m were given

上の見出しの内容と似ていますが、これは関数に渡された位置引数が多すぎる場合に出るエラーです。

設定している引数の数を見直してみましょう。
また、Discord.pyのイベント発生時に呼ばれる関数でなければ、キーワード引数が必要なのに位置引数を渡している・・・ということが無いか確認します。


TypeError: '型名' object is not callable

呼び出すことのできるもの、つまりcallableは関数、メソッド、クラスなどです。
それ以外のオブジェクトに()を付けて呼び出そうとしている可能性があります。
コードを再確認してください。


RuntimeWarning: coroutine 'コルーチン名' was never awaited

※ 以前に誤った内容を記述していたため修正しました。すみません。

async/await 構文に関するエラー。
コルーチン関数を呼び出そうとしているが、 await を使用していません。

await を使用するか、 それをasyncio.create_task に渡せばこの例外は発生しません。

message.channel.send("Hello World")  # エラーが発生します
await message.channel.send("Hello World")  # エラーは発生しません

import asyncio
loop = asyncio.get_event_loop()
loop.create_task(message.channel.send("Hello World"))  # エラーは発生しません

どういうときに await が必要になるかについては、公式ドキュメントを参照します。
そのメソッドの説明に「This is a coroutine」あるいは「await メソッド名()」とあればそれはawaitが必要である可能性が高いです。

TypeError: object 型名 can't be used in 'await' expression

※ 以前に誤った内容を記述していたため修正しました。すみません。

async/await 構文に関するエラー。
RuntimeWarning: coroutine 'コルーチン名' was never awaited とは真逆で、
await を使用することが想定されていないオブジェクトに await を使用している可能性があります。

例えば、以下のようなコード:

channel = await client.get_channel(123456123456123456)

discord.errors.LoginFailure: Improper token has been passed.

渡されたトークンが間違っています。
Developer Portalで再度コピーペーストしましょう。


discord.errors.Forbidden: 403 FORBIDDEN (error code: 50001): Missing Access

その処理を行うための権限が足りていません。
例えば、メンバーをキック、BANするとき。もしくは役職操作を行う場合によくあるエラーです。

役職操作の場合、優先順位に邪魔をされている可能性があります。
サーバー設定>役職からbotの持つ役職を、操作したい役職より上にしましょう。

あなたがサーバーの管理者でないならば、管理者に頼んで権限をいじってもらいましょう。


AttributeError: '型名' object has no attribute '属性名'

もし型名がNoneTypeであれば、それはオブジェクトの取得に失敗しているかもしれません。
取得する際、IDや名前にミスが無いか確認します。

もしそうでなければ、その属性名が本当に存在する属性か、ドキュメントを見て確かめます。


補足
client.get_channel()などの関数は、BOTの準備が完了するまでNoneを返します。
例えば、プログラムの冒頭でいきなりget_channel()をしていませんか?

これを避けるには、readyイベントが発生した「後」に取得を行う必要があります。
on_ready内に書く事が出来なければ、

@client.event
async def on_message(message):
    await client.wait_until_ready()
    ch = client.get_channel()

このように、チャンネルの取得前にwait_until_ready()関数を使用すると上手く行くかもしれません。


これは個人的な内容なのですが、以前このようなエラーに遭遇しました。

AttributeError: '型名' object has no attribute 'expandtabs'

これは、Botインスタンス生成時に位置が固定されている引数を省略していたことが原因でした。
expandtabsなどという属性名はドキュメントにはなかったため焦りました。


discord.errors.HTTPException: 400 BAD REQUEST (error code: 10014): Unknown Emoji

まずは以下のリンクを参照してください。
Discord.py 1.3.0a ドキュメント

このエラーは、Discord側が受け付けない文字をリアクションに追加しようとすることで発生します。
もし上記のリンクの通りに記述していてもエラーが出るなら、それはDiscord側がその絵文字を認識できていません。

よくあるのが数字の絵文字によるエラーで、どのスタイルを使うかを明記した状態で無いとDiscord側が受け付けない絵文字になってしまうために発生します。
以下のように書く事で解決することができます。

one絵文字を使う例
await message.add_reaction("\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}")

使用できる絵文字名を検索する方法について
ここでは絵文字名を使用しているのですが、その絵文字名は
http://www.fileformat.info/search/google.htm で検索できます。

Googleカスタム検索バーが設置されているので、emoji (絵文字名)などと打ってみてください。

image.png
image.png


discord.errors.ClientException: Command help is already registered.

Bot Commands Frameworkを使うときに起こるエラーです。

Botインスタンスを作成したとき、実は内部でhelpコマンドを勝手に登録してくれているのですが、それが原因でエラーが発生します。

ありがたいようで、めんどくさくもありますね。

コマンド一覧を表示するタイプのhelpであれば、デフォルトのヘルプが参照しているクラスを継承したクラスを定義することで実装できます。
https://cod-sushi.com/discord-py-help-command-japanese/

もしそうでなければ、インスタンスを作成する際に以下のように引数を渡すと良いです。

from discord.ext import commands
bot = commands.Bot(command_prefix="!",help_command=None)

@bot.command()
async def help(ctx):
    await ctx.send("**このbotについて**\n このbotは、botです。")

# ....

SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-n: truncated \UXXXX escape

このエラーはファイルのパスを指定する際に発生しやすいです。

C:\.....というようなパスならば、

r"c:\...."

というように、パスの前にrを付けると動くかもしれません。

以下は、エラーが発生しないけど動かない、という事例集です。

エラーは発生しないけど動かない

on_messageを複数書くと反応しない

たまに、on_message関数を何度も定義してしまう人がいます。
あくまで定義しているのは関数であり、結果的に行っているのは追加ではなく上書きです。

なので、最終的には一番下に書いたon_message内の処理だけが動いてしまいます。

ではどうするのか。まとめてしまえばよいのです

import discord

client = discord.Client()
token = ""

@client.event
async def on_message(message):
    if message.content == "!test_1":
        print("1つめの処理")
    if message.content == "!test_2":
        print("2つめの処理")
    if message.content == "!test_3":
        print("このように、いくつもの処理をまとめて書ける。")

client.run(token)

一応listenという機能を使うと複数の関数で同じイベントを待機することができるのですが、複雑だしこれ以上掘り下げる意味が無いので取り扱いません。

clientとbotを両方使うと片方が動作しない

Bot Commands Frameworkを利用するときによくある問題です。
これが起こる多くの原因は最後にどちらかのrun()を行っていない点にありますが、そもそもclientとbotを併用する必要はほとんどの場合ありません

なぜなら、commands.botdiscord.clientを継承したクラスであり、つまり完全上位互換であるからです。

import discord
from discord.ext import commands

client = discord.Client()
bot = commands.Bot(command_prefix="!")

token = ""

@client.event
async def on_message(message):
    print("clientでメッセージの送信をイベントとして待機できるが、")

@bot.event
async def on_message(message):
    print("clientで利用できるイベントはbotでも利用できる。")

bot.run(token) #client.runしていないのでclient.eventは動作しない

run()はそこで処理をループさせ、接続を切るまで処理をブロックします。
複数のインスタンスを同時に稼働するには、await login()await connect()に分離することが可能ですが、複雑なのでここでは省略します。

コマンドを追加したらメッセージに反応しない

これもBot Commands Frameworkを利用するときによくある問題です。簡単な解決法を以下に示します。

import discord
from discord.ext import commands

bot = command.Bot(command_prefix="!")
token = ""

@bot.event
async def on_message(message):
    print("処理の最後に次の式を追加します:")
    await bot.process_commands(message)

@bot.command()
async def example_cmd():
    print("")

bot.run(token)

tasks.loop 動かないがエラーも出ない

@tasks.loop() デコレータを使用した関数は start() メソッドで定期実行を行うことができますが、定期実行中にエラーが発生しても デフォルトではエラーが出力されません。
以下はエラーを出力させるように変更する簡単な例です。

詳細は以下をご覧ください:
https://discordpy.readthedocs.io/ja/latest/logging.html#logging-setup

import logging
logging.basicConfig(level=logging.ERROR)
coolwind0202
身内向けにDiscord BOTを作ったときのアイデアなどを書いていきたいです 記事内容はDiscord.pyに偏ってると思います 個人的にはC#とかJavaScriptを勉強中です
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした