Discord.pyでBotを定期再起動&メンバー数の取得

この記事は約4分で読めます。

ググってもなかなか出てこない情報なので、どこかの誰かの役に立つかもしれないと思い、メモとして残しておく。必要になる場面はよくわからない。

if os.name == "nt":
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

while True:
    time.sleep(60)
    now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))).replace(second=0, microsecond=0)
    if now.minute != 0:
        continue
    bot = commands.Bot(command_prefix=(" "), intents=intents, help_command=None, loop=asyncio.new_event_loop())
    @bot.event
    async def on_ready():
        shori()
        await bot.close()
    bot.run(TOKEN)

importとかはわかると思うので略。

注意点

Windows環境でloopを閉じる際に問題があるらしく、event_loop_policyを変更しないとbot.close()で「RuntimeError: Event loop is closed」エラーが出て止まってしまう。Linuxなら必要ないはずだが、結局本番環境では動かさなかったので未検証。

これは指定の処理を実行したらBotを終了させてループ待機に戻り、分が0、つまり1時間毎に動作するようにしている。しばらくBotを動かし続けたい場合は、簡易的にやるならon_readyにasyncio.sleep()を入れるか、tasks.loopで1分周期確認してbot.close()するなりしよう。もちろんその際はtime.sleep()を消すように。

経緯と顛末

そもそもなんで書いたかというと、Botの導入数統計を得たかったから。しかしメンバーインテントは取得していないので、Bot起動時にだけ流れてくるGuild.member_countのデータの合計を記録することにした。

Dpy2.0stableであれば、わざわざBotを再起動せずともBot.fetch_guildでGuild.approximate_member_countを更新して取得できるのだが、私が使っている2.0a3575(更新停止宣言したときのやつ)では未実装なため、再起動が必要だった。もちろんそのうちstableにはするが、移行ガイド読むのがめんどくさかったのでとりあえず2.0aのままでやりたい。

で、1時間毎の再起動をしてmember_countの更新には成功し、DBにも書き込まれだしたのだが、5時間ほどで「member_count属性が存在しない」旨のエラーが出て止まってしまった。半日空けて15分間隔にすると1時間強で同様のエラーが出た。

どうやら短時間に5回程度再起動するとDiscord側から規制がかかり、member_countの値を渡してくれなくなるようだ。1時間に1000回接続するとTokenリセットで強制切断されるのは知っていたが、1時間に1回であっても部分的な規制対象になるのは意外だった。参考までにBotの所属ギルド数は1000である。テスト用の2サバBotでは10連続起動しても規制がかからなかったので、単純に回数で決まっているわけでもないようだ。あるいはコードを稼働させる前のテスト時点で10回前後やった再起動もカウントされているのか。

ともかく、再起動によってmember_countを得る試みは現実的でない。直接リクエストを投げてapproximate_member_countを取得することにした。

    member_num = 0
    for g in bot.guilds:
        member_num += (await bot.http.request(discord.http.Route('GET', '/guilds/{guild_id}', guild_id=g.id), params={"with_counts": "true"}))["approximate_member_count"]

これを1時間周期で稼働させているが、24時間経過しても規制がかかった気配はない。

……と思いきや1ヶ月後に403が返ってくるようになった。やりすぎたかもしれない。1時間毎のデータはあまり必要ないことがわかったので、しばらく休ませてから半日毎で再度試すつもり。

コメント

タイトルとURLをコピーしました