チャチャチャおもちゃの抹茶っちゃ

ゲームのこととかプログラミングのこととか。気が向いたら書く。ブログタイトルは友人が考えました。

【Discord Bot】PythonでDiscordのBotを作ろう

はじめに

この記事は CCS Advent Calendar 2021の13日目の記事です。

adventar.org

ちにくんの記事: 【ゲーム制作向け】イージングのすゝめ←前|次→うしくんの記事: 今年のよかったこと2021

Discord bot について

みなさんDiscordは使っていますか?CCSの人はサークルのDiscordサーバーがあるし、それ以外でもサーバーを作ったりして便利に利用している人がぼくのまわりでは多いのかなと思っています。

さて、DiscordではBotを登録することができます。ご存知でしょうか。

自分で作らなくても、投票Botなどかなりの数のBotを自由に導入することができます。いろいろ「Discord Bot」などで検索してみると出てきます。

Bot を自作する

さて、DiscordのBotですが、それでもやはり物足りないときがあります。 たとえばサーバーの仲間とプレイしているゲームに特化したBotとか。

なんと、Discordはbotを作れるよう公式APIを公開しています!!

discord.com

こいつをつかってやれば、好きなBotを作ることができそうです。

ちなみに、ぼくは一緒にDQ10をプレイする仲間と使うために、PT募集したりするBotを作りました。 CCSの皆さんにはほとんど関係ないですが、例として使っていきます。

github.com

ライブラリの話

さて、じゃあさっそくBotを作るか、となるんですが、上記のAPIを叩くものを一から作るのは結構面倒です。ただみんな考えることは一緒なので、先人たちがラッパーライブラリを作ってくれています。てことでぼくらはこの恩恵に預かっていくことにしましょう。

有名なライブラリは以下の2つだと思います。

  • discord.py
  • discord.js

今回はdiscord.pyを使う想定で話を進めていきます。

Bot を自作する

さて、それではかんたんにbotの作り方を載っけていきます。

Botにもいろんなものがありますが、今回は次のようなものを作ります。

  • メッセージで/で始まるコマンドを打つとなにかしてくれる

Bot登録

ここにアクセスします。

discord.com

「New Application」を押してアプリケーションを登録。必要な情報を入力しましょう。

あとは右側の「Bot」をひらいて「TOKEN」のところからBotトークンをコピーしておきます。このトークンを公開しないように!!!!

f:id:mattyan1053:20211213001250p:plain
トークンをコピー

Bot をサーバーに登録

同じページの右側のメニューから、「OAuth2」を選択。「URL Generator」から 「bot」を選んで、必要な権限を設定して、生成されたURLにアクセスすると、自分が管理者になっているサーバーにBotを追加することができます。

これで準備が整いました。

Bot のコーディング

Pythonはすでに使えるものとして、更にライブラリが必要です。

まずはじめに、discord.pyをインストールしておきましょう。

$ pip install discord.py

.envに変数を書いて、管理できるようにしておきたいので、次のライブラリもいれておきます。

$ pip install dotenv

ちなみにpipenvを使える人なら、先程のDQ10-botリポジトリをからpipfileをダウンロードしてきて、pipenv installすれば一通り環境は揃います。使っているのはPython3.8です。

余談ですが、ぼくは基本的に仮想環境を使う派なので、Pythonでなにかするときはたいていpipenvを使っています。

コーディングの前準備

最初に、先程取得しておいたトークンを読み込む部分を作っておきます。

コードに直書きは嫌なので、.envにでも設定しておいてとってくることにしましょう。

DISCORD_BOT_TOKEN = *****(先程コピーしたやつ)

あとはPython側から読み込むだけ。

import os
from dotenv import load_dotenv

load_dotenv()

DISCORD__BOT_TOKEN = os.getenv('DISCORD_BOT_TOKEN')

これでOKですね。無事読み込めます。 .envファイルはバージョン管理に含めずにちゃんと管理してね

コーディング

あとはBotの根幹部分を書いていくだけです。

ここで今回特に紹介したいのが、discord.pyの拡張であるcommandsクラスです。 文字列を取得して、コマンドに変換するコードを直書きしなくても良くなる上、helpコマンドなどをなんと自動で生成してくれます!たすかる!(argparseとかで似たようなの見たことありますかね)

結構ネット上でもon_messageとかBotに限らないイベントに対応した書き方の記事は多くても、コマンド拡張の話はあまり見ないので......。

import discord
from discord.ext import commands


class MyCommands(commands.Cog):
    def __init__(self, client):
        super().__init__()
        self.client = client

    @commands.command(name="hello")
    async def hello(self, ctx, arg1, arg2):
        await ctx.send(f"hello {arg1}, {arg2}")
        return
        

intents = discord.Intents.default()
intents.typing = False
client = commands.Bot(command_prefix='/', intents=intents)
client.add_cog(MyCommands(client=client))
client.run(DISCORD_BOT_TOKEN)

適当なコードですが、だいたいの書き方がこんな感じです。

少しずつ説明していきます。

intents = discord.Intents.default()
intents.typing = False
client = commands.Bot(command_prefix='/', intents=intents)
client.add_cog(MyCommands(client=client))
client.run(TOKEN)

Botを動かしているメインの部分です。intentsの2行で受け取るイベントの種類が制御できます。まあ小規模なBotとか公開しないBotならそんな気にしなくていいです。おまじない。

あとはコマンドの開始文字列(prefix)を/にして設定、コマンドとして自作のクラスを設定し、最後にトークンを渡しています。

class MyCommands(commands.Cog):
    def __init__(self, client):
        super().__init__()
        self.client = client

    @commands.command(name="hello")
    async def hello(self, ctx, arg1, arg2):
        await ctx.send(f"hello {arg1}, {arg2}")
        return

コマンド部分のクラスです。/hello arg1 arg2とDiscord上で入力したときに実行されるやつ。/hello mattyan 1053とかね。特に意味はない、サンプルなので。

今回はcommands拡張を用いているので、指定のprefix(=/)のメッセージが来たときに自動で呼ばれます。しかも、コマンド引数も自動で分けてクラスメソッドの引数に渡してくれます!引数も可変長とか諸々対応してたはず。

面倒な文字列処理なくて助かる!ctxはDiscordのコンテキストなので、通常のライブラリと同じ。公式ドキュメントにいろいろあります。

discordpy.readthedocs.io

ということで、あとはあまり深いことは考えずにBotにしてほしい処理を記述してあげるだけでOK!APIとか考えずそれっぽいメソッドを呼んじまえ!ラッパー最高!

ここからリアクションをつけたりもできます。

サーバーに置く

あとは出来上がったのをサーバーにおいて実行しっぱなしにしておきましょう。 サークル用Botならサークルのサーバーとかでもいいんじゃないかな。Web管に相談しましょう。私用なら適当にGCP無料枠とかでも余裕で動きます。軽いので。

ぼくのpipfileに従うならpipenv run startで動きます。

おわりに

お疲れさまでした!これでみなさんも自由にBotがつくれますね!

サークルの総会用Botとか誰か作ったらおもしろそう。委任状機能付きの投票機能とか?

余談

Discord、サーバー内絵文字を使うとき:emojiname:みたいに指定するだけでいいけれど、Bot側から飛ばすときは<:emojiname:emojiid>みたいに指定しないといけないらしい。絵文字IDをとってきて添えないと......。なんかもっといいやり方あるかも。emojiクラスよく読んでない。