複数のDiscord BotをHerokuの無料プラン内で動かす

日常

はじめに

discord.pyを用いてDiscord用BOTの開発をしていますが、
開発が進んでいく流れで機能をアカウントで分けることにしました。
herokuで1プロセスで複数アカウントを同時に動かす際にいろいろ試行したのでそれを記します。

手順を追っていくことでどう変更するかが分かる構成になっています。
最終的にどういう形になるかが知りたい方は最終的なファイル構成とコードに進んでください。

なお、discord.pyによるDiscord用BOTの基本的な作り方についてはこの記事では割愛します。

【手順】
・最初のサンプルコード
・別のpyファイルから起動するようにする
・複数アカウントで動かす

【前提】
Discord BOTのアカウントを2つ準備しておきます。
各アカウントのトークンを控えておいてください。

最初のサンプルコード

以下に各アカウントごとに基本的な動作をするサンプルコードを記します。

import discord
token = "BOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("おはよう"):
		await message.channel.send("おはようございます")

# bot起動用
client.run(token)
import discord
token = "BOT2TOKENBOT2TOKENBOT2TOKENBOT2TOKENBOT2TOKENBOT2TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("こんにちは"):
		await message.channel.send("はいこんにちは")

# bot起動用
client.run(token)

Discord内で指定の文字列から始まるメッセージを受信したときに指定の文字列を送信するコードになります。
BOT1は「おはよう」というメッセージが受信したときに「おはようございます」とメッセージを送信します。
BOT2は「こんにちは」というメッセージが受信したときに「はいこんにちは」とメッセージを送信します。
discord.pyでDiscordのBOTを作る最初のコードとして一番シンプルな構成かと思います。

当然ですが、この段階ではBOT1とBOT2で別々に起動することになります。
BOT1を起動しているときだけBOT1が「おはよう」に反応し、
BOT2を起動しているときだけBOT2が「こんにちは」に反応します。

ここから同時に起動するように変更を加えていきます。

別のpyファイルから起動するようにする

サンプルコードを以下のように変更します。
変更箇所について解説します。


import discord
token = "BOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("おはよう"):
		await message.channel.send("おはようございます")

# bot起動用
def client_run():
	client.run(token)

BOT1を例に解説します。
初めのサンプルコードでは

client.run(token)

が実行された時点でBOTを起動していましたが、
今回の変更によって

def client_run():
	client.run(token)

を呼び出すことでBOTが起動するようになりました。
これによってtestbot1.pyを実行しても起動しなくなります。
testbot2.pyも同様に変更してください。

次に、これらBOTファイルをフォルダに入れてしまいましょう。
「bot」フォルダを作成し、testbot1.pyとtestbot2.pyを移動します。
その中で__init__.pyというファイルを作成します。

from . import testbot1
from . import testbot2

botフォルダの中は__init__.py、testbot1.py、testbot2.pyの3つがあります。

一つ上のフォルダに戻り、BOTを起動するための.pyファイルを作成します。

import bot #作成したbotフォルダをimport

def main():
	bot.testbot1.client_run()

main()
import bot #作成したbotフォルダをimport

def main():
	bot.testbot2.client_run()

main()

BOTを起動するための.pyファイルです。
testbot1の中のclient_run()を呼び出し、BOTを起動します。
run_testbot1.pyを実行することでBOT1が起動するようになりました。
同様にrun_testbot2.pyを実行することでBOT2が起動するようになったと思います。

この段階でもまだ別々での起動です。
現状でのファイル構成を記しておきます。

botフォルダ
├bot
│├__init__.py
│├testbot1.py
│└testbot2.py
├run_testbot1.py
└run_testbot2.py

複数アカウントで動かす

asyncで実行する関数を追加します。


import discord
token = "BOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("おはよう"):
		await message.channel.send("おはようございます")

# bot起動用
def client_run():
	client.run(token)

# bot起動用
async def async_client_run():
	await client.start(token)

以下の関数を追加しました。複数動かす場合はasyncのこちらを使用します。

# bot起動用
async def async_client_run():
	await client.start(token)

必ず複数動かすのであればdef client_run():は消してしまっても構いませんが、
アカウント単体で動かしたい場合もあると思うので残しておいてもいいでしょう。
例によってtestbot2.pyも同様に追加してください。

次にBOTを起動するための.pyファイルを作成します。

import bot #作成したbotフォルダをimport
import asyncio

def main():
	loop = asyncio.get_event_loop()
	
	loop.create_task(bot.testbot1.async_client_run())
	loop.create_task(bot.testbot2.async_client_run())
	
	loop.run_forever()
	loop.close()

main()

今回の要になります。
BOTを複数同時に動かす.pyファイルです。
この.pyファイルを実行することでBOTの複数同時起動ができるようになります。

最終的なファイル構成とコード

最終的なファイル構成とコードは以下のようになります。
BOTのトークンは各種変更するようにしてください。

run_master.pyファイルを実行することで複数アカウントのBOTが起動できます。
run_testbot1.pyやrun_testbot2.pyを実行することでそれぞれ単体で起動できます。

ファイル構成やimportの指定などは環境によって適宜変更するようにしてください。

botフォルダ
├bot
│├__init__.py
│├testbot1.py
│└testbot2.py
├run_master.py
├run_testbot1.py
└run_testbot2.py
from . import testbot1
from . import testbot2
import discord
token = "BOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKENBOT1TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("おはよう"):
		await message.channel.send("おはようございます")

# bot起動用
def client_run():
	client.run(token)

# bot起動用
async def async_client_run():
	await client.start(token)
import discord
token = "BOT1TOKENBOT2TOKENBOT2TOKENBOT2TOKENBOT2TOKENBOT2TOKEN"
client = discord.Client()

@client.event
async def on_ready():
	"""
	起動時に実行される
	"""
	print('----------------------------------')
	print("discord.py Verion")
	print("\t" + discord.__version__)
	print("")
	print('Logged in as')
	print("\tName\t" + str(client.user.name))
	print("\tID\t" + str(client.user.id))
	print('----------------------------------')

@client.event
async def on_message(message):
	"""
	メッセージの受信時に実行される
	"""
	
	# bot自身の発言には反応しない
	if message.author.bot:
		return
	
	# コマンドのとき
	if message.content.startswith("こんにちは"):
		await message.channel.send("はいこんにちは")

# bot起動用
def client_run():
	client.run(token)

# bot起動用
async def async_client_run():
	await client.start(token)
import bot #作成したbotフォルダをimport
import asyncio

def main():
	loop = asyncio.get_event_loop()
	
	loop.create_task(bot.testbot1.async_client_run())
	loop.create_task(bot.testbot2.async_client_run())
	
	loop.run_forever()
	loop.close()

main()
import bot #作成したbotフォルダをimport

def main():
	bot.testbot1.client_run()

main()
import bot #作成したbotフォルダをimport

def main():
	bot.testbot2.client_run()

main()

herokuではrun_master.pyを実行させることで複数のBOTが起動できるようになります。
Procfileにrun_master.pyを追加しておいてください。

1プロセス内で複数アカウントを起動しているとき、出力ログは起動しているBOT全て同じところに出力されるので、どのアカウントからのログなのか基本的には分からなくなります。
どのアカウントからの出力ログなのか、BOTの各機能での出力の時点で分かるようにしておく必要があります。

コメント

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