0.この記事で解説すること
- discord.pyのAPIリファレンスの読み方が分かる
- いきなりrun(token)が動かなくなった時の対応方法
- Botのログオフ機能を実装する
1.今回、解説に至った経緯
- デバッグしたいのでbotをログオフさせたい
- 解説記事を色々と見たが実装してる方がいなかった
- システムの強制終了を頻繁にしているとrun(token)で動かなくなった
- 対策ができたので記事を書くことに
※対策の追記内容があります! 対策の追記
3.についてですが、Replitで動かしてると発生するようです(他環境でも起きるかも?)
内容としては以下のエラーログが出ます(一部抜粋)
discord.errors.HTTPException: 429 Too Many Requests (error code: 0):
2.ログオフ機能を実装する
2.1 開発環境
- 統合開発環境: Replit
- discord.py: 2.0.1
- 言語: python3.8
2.2 処理の詳細内容
- Discordのユーザー入力を検知
- Botをログオフさせる
- コード上でログオフを検知してシステム自体を落とす
2.2.1 Discordのユーザー入力検知
on_message(message)で取得できます。
以下の内容を踏まえて元となるコードを書きます。
import discord
import os, sys, datetime, json
class MyClient(discord.Client):
#メッセージが書き込まれた時
async def on_message(self, message):
#送信者がbot自身の場合はコマンドを無効にする
if message.author.bot:
print("bot送信\n")
return
if ".logoff" in message.content:
#ログオフ
await self.Logoff(message)
return
def main():
#環境変数からtokenを取ってくる
TOKEN = os.getenv("TOKEN")
#すべての機能を使えるようにする
intents = discord.Intents.all()
#intentsは必須パラメータ
client = MyClient(intents=intents)
#Discord接続
client.run(TOKEN)
if __name__ == "__main__":
main()
on_message(message)で使われる引数はクラスです。
メンバ変数やメソッドはどう使うかはAPIリファレンスを読めばわかります。
discord.Message
今回の場合だと
A.送信者がBotかを判定
B.メッセージ内容に.logoffとあればログオフさせる関数を実行
それぞれの処理詳細について説明していきます。
2.2.1(A) Bot判定
import discord
import os, sys, datetime, json
class MyClient(discord.Client):
#メッセージが書き込まれた時
async def on_message(self, message):
#送信者がbot自身の場合はコマンドを無効にする
if message.author.bot:
print("bot送信\n")
return
APIリファレンスが読めないと、この記述にたどり着けないと思います。
(他の解説記事を読むと別の記述でBot判定していました)
まずはmessage.authorから見ていきましょう。
こちらを見ると今回の場合はMemberが取得できるようです。
リンクをクリックしてMemberの中身をみていきましょう。
discord.Memberクラスを取得できるようです。
この中にあるメンバ変数BotはBotの場合Trueを返します。
長かったので今までの流れをまとめると以下の記述となります。
discord.Messageクラスはdiscord.Memberクラスを持っている。
→ discord.Memberクラスのメンバ変数botを使ってBot判定をする。
図解すると、こんな感じです。
on_message(message)
message = discord.Messageクラス
L message.author = discord.Memberクラス
L bot(Botの場合はTrue)
2.2.1(B) メッセージ内容判定
import discord
import os, sys, datetime, json
class MyClient(discord.Client):
#メッセージが書き込まれた時
async def on_message(self, message):
if ".logoff" in message.content:
#ログオフ
await self.Logoff(message)
return
message.contentはメンバ変数でメッセージ内容を取得できます。
2.2.2 Botをログオフさせる
まずはコードから
import discord
import os, sys, datetime, json
class MyClient(discord.Client):
#ログオフ
async def Logoff(self, message):
print("実行:Logoff\n")
await message.channel.send("ログオフします")
await self.close()
ココでは2つの処理をしています。
A.チャンネルにメッセージを送信
B.ログオフする
では1つずつ見ていきましょう。
2.2.2(A) チャンネルにメッセージを送信
messageはdiscord.Messageクラスです。
message.channelを見てみましょう。
今回の場合だとdiscord.TextChannelクラスが取れます。
(メッセージを送信したテキストチャンネルの情報)
discord.TextChannelのsend()メソッドでメッセージを送信しています。
2.2.2(B) ログオフ
self.close()となってますが、これはdiscord.Clientクラスのclose()メソッドです。
(自作クラスMyClientはdiscord.Clientを継承している)
2.2.3 コード上でログオフを検知してシステム自体を落とす
まずはコードから
import discord
import os, sys, datetime, json
class MyClient(discord.Client):
#先ほど説明したので省略
def main():
#環境変数からtokenを取ってくる
TOKEN = os.getenv("TOKEN")
#すべての機能を使えるようにする
intents = discord.Intents.all()
#intentsは必須パラメータ
client = MyClient(intents=intents)
#Discord接続
client.run(TOKEN)
#pythonを落とす
if (client.is_closed()):
#discord.errors.HTTPException: 429 Too Many Requests対策
print("osを切ります")
os.system("kill 1")
if __name__ == "__main__":
main()
discord.Clientクラスのis_closed()メソッド
これはcloseされた場合にTrueを返します。
この判定が通ったときに呼び出されるos.system("kill 1")
コレはに引数内容のコマンドを実行させます。
今回の記述内容は「今動いてるシステムを終了させる」です。
※対策の追記内容があります! 対策の追記
2.3 実際に動かしてみた
起動時
Discordの画面
StopからRunに変わったので実装できてます。
ココまでお付き合いいただき、ありがとうございました。
※何か連絡や依頼がありましたらDMをお願いします。
Twitter: Don't Pray Debug