Discord公式APIのラッパー、Discord.pyを使ってBOTを作成し始めた人がよく突き当たる問題とその解決法をまとめました。
(環境構築ではなく、実際に動作させる場合に関する問題についての記事です)
もし新たにエラーや原因不明の挙動を発見したら追記します。
環境・対象
- Discord.py ver1.2.3
- Python ver3.7
- Windows10
対象はDiscord.pyでbotを作り始めたもののエラーの意味がわからない、エラーは消えたけど動かない、という人です。
これを読む前に、
- エラー文で検索する
- 起こった事象を単語で検索する
- ドキュメントのよくある質問を読む
何度検索しても、この記事を読んでも解決しなければ以下のサーバーに質問するのが良いです。
少し高度な問題にぶつかっている可能性があります。
よくあるエラー
KeyError: 'animated'
最近のDiscord公式APIの変更によりよく発生するようになったエラーです。
現在のDiscord.pyではこの問題が修正されていますので、Discord.pyをアップデートしてください。
pipを使用する場合は以下のようにアップデートできます。
python3 -m pip install -U discord.py
Windowsの場合は以下のようにすると上手く行くかもしれません。
py -3 -m pip install -U discord.py
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
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
どちらもawait
キーワードに関するエラーです。
上のエラーはそれを呼び出す際にawait
が必要でないのにawait
しています。
下のエラーはawait
が必要なのにawait
していません。
例えば、message.channel.send()
はawait
を必要とします。
print()
はawait
を必要としません。
どういうときに必要になるかについては、公式ドキュメントを参照します。
そのメソッドの説明に「This is a coroutine」あるいは「await メソッド名()
」とあればそれはawait
が必要である可能性が高いです。
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側が受け付けない絵文字になってしまうために発生します。
以下のように書く事で解決することができます。
await message.add_reaction("\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}")
使用できる絵文字名を検索する方法について
ここでは絵文字名を使用しているのですが、その絵文字名は
http://www.fileformat.info/search/google.htm で検索できます。
Googleカスタム検索バーが設置されているので、emoji (絵文字名)
などと打ってみてください。
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.bot
はdiscord.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)