更新
2023/01/28 Ver.2向けに内容を書き換えました。
1. 環境構築
Pythonとdiscord.pyのインストールを済ませておいてください
Pythonのインストールについてはここに書くまでもないので、各自調べてください。
Pythonのインストールができていれば
python3 -m pip install -U discord.py
とかでdiscord.pyのインストールが出来るはずです。
condaでもconda-forgeにあるはずです
参考:
discord.py "はじめに"
conda-forge / packages / discord.py
2. トークンの取得 & サーバー加入
このページを参考にトークンを取得して、自分で適当なテスト用discordサーバーを作ってそこに入れてください。
具体的には、discordのapplicationのページに行って、右上のNewApplication
から、新たなアプリケーションを作成、設定を済ませてから、左のメニューからBot
を選び、Add Bot
でBotを追加(名前が被ってると怒られるので、Application
から直す)します。
下のPrivileged Gateway Intents
というところに行き、3つともオンにします(受け取れる情報の範囲の設定です、この記事の内容および個人利用であれば全てオンで大丈夫です。)
それができたらBotとして設定とトークンの取得を行い、左のメニューからOAuth2
→URL Generator
のページに行き、付与する権限を決めてURLを生成すると、サーバー加入のためのURLを生成できます。
3. コードを書く
#discord.pyの大事な部分をimport
from discord.ext import commands
import discord
import os
# 環境変数を参照する(最悪トークンベタ書きでも可)
APITOKEN=os.environ["DISCORD_BOT_TOKEN"]
#botのオブジェクトを作成(コマンドのトリガーを!に)
bot=commands.Bot(command_prefix='!',intents=discord.Intents.all())
#コマンドを設定
@bot.command()
#"!hello"と送信された時
async def hello(ctx):
await ctx.send("hello!")#送信された場所に"hello!"と送り返す
#イベントを検知
@bot.event
#botの起動が完了したとき
async def on_ready():
print("Hello!")#コマンドラインにHello!と出力
#起動
bot.run(APITOKEN)
これだけです。
これだけで最低限のbot起動が出来るはずです。
デプロイ等の際は環境変数にトークンを置いておいて参照するほうがいいと思います。
環境変数ワカンナイヨーとか、個人利用の範疇ではベタ書きしても大丈夫だとは思いますが注意が一つだけあります。PublicなGitHubリポジトリ等、トークンを公開しないように最大限注意してください。トークンを公開してしまうとbotを悪用されかねないので、公開しないよう気を付けてください。ちなみに公開するとDiscordのトークン公開を検知するやつがトークンを無効化してくれて、めっちゃ怒られました。
これらの手順で詰まるところとしてはトークンの取得と環境構築だと思います。
が、discord.py とかで調べるだけで先駆者の記事がうじゃうじゃ出てくると思うので、どうにかなると思います(丸投げ)
とりあえず、たったこれだけでdiscordbotの原型ができます。
細かい処理などについては少しPythonを理解する必要がありますが、簡単なメッセージを返す程度ならかなり簡単にできると思います。
4. いろいろな処理について
ここからはこういうことができるよ!という紹介です。この節のコードは基本先ほどのコードのbotの定義とrunの間に置けば動くはずです。
コマンドの構造
@bot.command() #↓任意のコマンド名(foo)
async def foo(ctx):#←引数ctxは必須(情報を渡すため)
#任意の処理
await ctx.send("foo!")#任意のメッセージを返す
@bot.command() #↓任意のコマンド名(bar)
async def bar(ctx,arg):#←ctxの後に追加することで引数を取れる
#任意の処理
await ctx.send(f"***{arg}***")#discordの書式に則っていればテキストの修飾もできる
これで新しいコマンドをじゃんじゃん追加できます。これらのコマンドはbot作成時に指定したcommand_prefix(今回は!)と設定したコマンドで動作します。("!foo"、"!bar xxx"のようにして使用)
async、awaitの話はめんどくさいので省きますが、とにかく送信するときはawaitを使えば、たいてい動きます。
もちろん引数(処理に必要な情報)もとることができ、スペース区切りで入力されたデータが引数のctxに続く部分に入れられていきます。(もちろん入力とかみ合わなければエラーを吐きます)
コマンド入力から情報を取る
#ctx.authorでコマンド送信者の情報が取れる
@bot.command()
async def info(ctx):
info=f"this guild:{ctx.guild}\n"
info+=f"this channel:{ctx.channel}\n"
info+=f"your user id:{ctx.author.id}\n"
info+=f"your user name:{ctx.author}"
await ctx.send(info)
#送信者にDMを返す
@bot.command()
async def dm(ctx):
await ctx.author.send("Direct Message!!")
送信されたチャンネルや、ユーザーの情報は引数ctxとして渡されます。
ctx.authorでユーザーの情報が取れるので、そのままDMを送ったりできます。
ユーザーの情報を格納しておき、必要になった際に送れます。
contextクラスなど、かなり持ってる情報が充実しているので、かなりいろいろ出来ると思います。
詳細については以下の参考に示しているリファレンスを参照してください。
参考:
discord.py APIリファレンス "Context"
discord.py APIリファレンス "User"
特定のワードに反応(イベントに反応)
#何らかのメッセージが送られた時に発動
@bot.event
async def on_message(message):#引数は上記のctxみたいなもの、これはMessageクラス
if message.author.bot:
pass
elif "おはよう" in message.content:#メッセージの内容に"おはよう"という文字列が含まれているとき、
await message.channel.send("おはよう!!!")#メッセージが送られたチャンネルに送信
await bot.process_commands(message) #コマンド側にもメッセージを渡す
ここまではすべてコマンド(!hello など意図的に動作させるもの)でしたが、特定の文章に反応するようなものも可能です。
ここでは、botの発言にbotが反応して無限ループを防ぐためにbotの発言をスルーするようにしています。
これだけだと、コマンドを設定している場合でもこちらに全て吸収されてしまうので、最後にコマンド側にメッセージを渡してあげます。
今回はメッセージが送られた時というトリガーですが、以下のイベントリファレンスのリンクに対応できるイベントが色々書いてあります。
参考:
discord.py APIリファレンス "イベントリファレンス"
discord.py APIリファレンス "Message"
discord.py APIリファレンス "よくある質問" "on_message を使うとコマンドが動作しなくなります。どうしてですか。"
指定時間に動作
from discord.ext import commands,tasks
まずtasksを追加でimportします。(import部分を書き換えてください)
#ユーザーの情報を格納するset
users=set()
#アラーム設定用コマンド
@bot.command()
async def alarm(ctx):
users.add(ctx.author)
print(f"{ctx.author}のアラームを登録しました。")
await ctx.send("アラーム登録しました!")
#60秒ごとに実行
@tasks.loop(seconds=60)
async def morning():
now=time.localtime()#現在時刻を取得
if now.tm_hour==4 and now.tm_min==0:#4時なら
for user in users:#アラームを登録している全ユーザーに対して
await user.send("おはよう!朝4時に何してるんだい?")#おはよう!朝4時に何してるんだい?
#ループの開始
morning.start()
一定時間ごとに行うループには引数がないので、送信先をこちらで用意しておく必要があります。ここではアラーム設定用コマンドを用意し、ユーザーの情報をsetに格納しています。そして、60秒ごとに現在時刻を取り、指定時刻なら各ユーザーのDMにメッセージを送信しています。もちろんチャンネルの情報を持っていればそこに送ったりもできます。
指定時刻に実行するためのtimeってやつがあるっぽいんですが、ねーよボケ(loop() got an unexpected keyword argument 'time')と怒られたのでこの方式を取っています。ほかにもループ用の引数がいくつかあります。(参考参照)
そして最後の行にある通り、スタートさせないと動きません。
参考:
discord.py "discord.ext.tasks"
5. Cogを使う
Cogとは、歯車の意で、botのパーツとなります。Cogを使ってbotを分割することで、ファイルや機能の管理がしやすくなったり、一つだけ停止させてメンテナンスできたり(この記事では扱いませんが)、メリットが多いです。
discordbot
│ discordbot.py
│
└─cogs
cogA.py
cogB.py
cogC.py
このようなファイル構造だとします。もちろんコグもPythonファイルです。
async def main():
# コグのフォルダ
cogfolder = "cogs."
# そして使用するコグの列挙(拡張子無しのファイル名)
cogs = ["sample_cog"]
for c in cogs:
await bot.load_extension(cogfolder + c)
# start the client
async with bot:
await bot.start(APITOKEN)
まず、メインファイル(discordbot.py)のrunの部分をこれに置き換えます。必要なコグをリストにまとめておき、load_extension()でコグをロードしています。
(また、起動時に他のawaitしたい操作があればこのmain関数内でできます。)
そしてコグの中身はcommands.Cogを継承したクラスとして記述します。
from discord.ext import commands
#checkcogの本体
class checkcog(commands.Cog):
def __init__(self,bot):
self.bot=bot
# Cogが読み込まれた時に発動
@commands.Cog.listener()
async def on_ready(self):
print('checkCog on ready!')
#コマンドの記述
@command.command()
async def cog_command(self,ctx):
await ctx.send("using cog!")
#Cogとして使うのに必要なsetup関数
def setup(bot):
print("checkcog OK")
return bot.add_cog(checkcog(bot))
コマンドの記述の中以外はほぼ定型文です。コグ名としてクラスの名前だけはちゃんと変えてあげてください。