導入
Docker上でDiscordのBotを動かす のアイデアを元に誰かがボイスチャットに入ったときに入場曲を流すBotを、勉強を兼ねてPython/Docker/AWS EC2の環境で作成してみました。
ローカルで実行したいだけの場合や、ローカルのDocker環境で実行したい場合はDockerのくだりやEC2のくだりを適宜省略すれば動かせます。
下準備
discordのBotを作成し、アクセストークンを控えておく。
参考: 簡単なDiscord Botの作り方(初心者向け)EC2インスタンスを作成し、SSH接続できるようにしておく。今回は無料枠のAmazon Linux 2 t2.microを選びます。
参考:【初心者向け】Amazon EC2にSSH接続する【Windows、Macintosh】EC2インスタンス上にdocker環境を構築しておく。こちらの2,3をご参考に。
参考: AWS EC2インスタンスにdockerとdocker-composeをインストールして簡単なWEBサービスを立ち上げる方法
これで進捗90%です。
Pythonコードの作成
discord.py の使用を前提で作成します。
discord.PCMVolumeTransformer(xxx, volume=0.04)
の部分で音量を絞っています。結構小さめですが、これくらいがちょうどよいです。
if before.channel is None:
で、接続なしの状態からそのチャンネルに入った場合のみの再生に限定しています。
sleep()
で時間を決めて退出していますが、もっといいやり方があるかも。
今回は複数の音源をランダムに再生するように決めています。確率はお好みで。人によって再生する音源を変えても良いです。
import discord
import random
from time import sleep
# Botのアクセストークン
discord_token = 'XXXX0XXX0000XX00.XXX00XX0.x000XXX0-XXXXX00000XXX0X000X'
client = discord.Client()
@client.event
async def on_ready():
print('Botを起動しました')
@client.event
async def on_voice_state_update(member, before, after):
if member.bot:
return
if before.channel is None and after.channel.id == 000000000000000000: # 接続するボイスチャンネルのID
sleep(1)
await member.voice.channel.connect()
print('接続しました。')
num = random.randint(0,5)
if num == 0:
member.guild.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio("makenai.mp3"), volume=0.04))
sleep(22)
elif 1 <= num <= 2:
member.guild.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio("tigau.mp3"), volume=0.04))
sleep(20)
elif 3 <= num <= 4:
member.guild.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio("arigatou.mp3"), volume=0.04))
sleep(20)
elif num == 5:
member.guild.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio("ambitious.mp3"), volume=0.04))
sleep(14)
member.guild.voice_client.stop()
await member.guild.voice_client.disconnect()
print('接続解除しました。')
client.run(discord_token)
Dockerfileの作成
dockerイメージの元となるDockerfileを作成します。拡張子のないただのテキストです。
今回は音声を扱うのでffmpegをインストールし、音声を扱えるdiscord.py[voice]を入れます。
ローカルで実行する場合もこれらをインストールしておけば動きます。
FROM python:3.9.4-buster
WORKDIR /app
COPY . /app
RUN apt-get update && apt-get install -y ffmpeg
RUN python -m pip install \
--upgrade pip \
--upgrade setuptools \
discord.py[voice]
CMD ["python3.9", "entrancebot.py"]
docker-compose.ymlファイルの作成
直接Dockerfileで構築する場合は不要ですが、使っておきます。
ほぼbuild: .
でDockerfileを指定しているだけです。
version: "3"
services:
python-service:
restart: always
build: .
container_name: "pythonContainer"
working_dir: "/app"
tty: true
ディレクトリの作成
作った3ファイル+音源ソースファイルを同じディレクトリにぶち込んでおきます。今回はentrancebot
ディレクトリとします。
ディレクトリのコピー
下準備のssh接続完了後、entrancebot
ディレクトリの上階層で下記を実行し、ローカルのディレクトリをEC2インスタンス上のディレクトリにコピーします。
指定するユーザー名はデフォルトではec2-user, IPアドレスはご確認を。
$ scp -r -i ~/.ssh/xxxx.pem entrancebot ec2-user@xx.xxx.xxx.xx:/home/ec2-user
dockerイメージ・コンテナの作成
まずはEC2インスタンスの中に入ります。
$ ssh -i ~/.ssh/xxxx.pem ec2-user@xx.xxx.xxx.xx
入るとホームディレクトリ/home/ec2-user
にいるはずです。
そこで
$ cd entrancebot
で、先程コピーしたディレクトリの中に移動します。
さらに、
$ docker-compose up -d --build
で、dockerイメージとコンテナが作成され、起動されるはずです。
これでPythonコード上で指定したボイスチャンネルに入場すれば、Botが歌ってくれます!
ちなみに
$ docker-compose exec python-service /bin/sh
でdockerコンテナの中に入ることもできます。
片付け
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
daad8f6d5c15 entrancebot_python-service "python3.9 entranceb…" 21 seconds ago Up 20 seconds pythonContainer
で表示されたIDを使って、
$ docker container stop daad8f6d5c15
$ docker container rm daad8f6d5c15
コンテナを停止して、削除。さらに、
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
entrancebot_python-service latest e235b4decbcd 4 minutes ago 1.15GB
python 3.9.4-buster 8f3a05c42ee5 32 hours ago 886MB
で表示されたIDを使って、
$ docker image rm e235b4decbcd
$ docker image rm 8f3a05c42ee5
イメージを削除。
あるいはコンテナ・イメージ削除はdocker-composeコマンドでまとめてできます。
$ docker-compose down --rmi all
また、cd ../
でentrancebotディレクトリの上階層に移動して
$ rm -r entrancebot
でディレクトリを削除。最後に、
$ exit
EC2を抜けて、ローカルに戻れます。お疲れさまでした。
実行内容を変えたい場合はリファレンスをご参考に。
不明点・うまく行かなかったところ
ローカル(macOS)のdocker環境で実施したときは生成されるimageは
entrancebot_python-service
1つのみで、python
が別に先に持ってこられることはなかったのだが、EC2上ではpythonイメージが別にある。この違いはなに...?どちらも空の状態から実行したけれど。。理解が足りていない。。ローカルのdocker環境では問題はなかったが、EC2インスタンス上ではentrancebot.pyで
connect()
前にsleep()
等で間隔開けないとうまく再生されなかった。性能の問題...?
Python/Docker/EC2 すべてが初めて触るものだったので、特定できず。。
詳しい方、コメントいただければと思いますm(_ _)m