参考にした記事
- https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f
- https://qiita.com/junpiiiiiiik/items/79c2219da6b5e2c06ed8
概要
掲題のとおりですが、
- Discord botを作る。
- 特定の文字列がメッセージに含まれたときに、MeCabで形態素解析した結果を返す。
- MeCabの辞書としてNEologdも使えるようにしておく。
- 処理言語はPythonを使う。
- heroku上で動作させる。
- herokuのContainer Registryを使ってDockerイメージをデプロイする。
root@4c506a68cd26:~# python --version
Python 3.7.7
root@4c506a68cd26:~# mecab --version
mecab of 0.996
準備
「参考にした記事」に倣うのみなので、割愛。
- https://qiita.com/junpiiiiiiik/items/79c2219da6b5e2c06ed8#%E8%A8%AD%E5%AE%9A
- https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f#bot%E7%94%A8%E3%81%AE%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E6%89%8B%E3%81%AB%E5%85%A5%E3%82%8C%E3%82%8B
1点わからなかった、というか勘違いしていたのは、ローカルやherokuにデプロイしたときにエンドポイントを指定する設定がどこかにあるものだと思っていろいろ調べてしまった。
どこにも書いていなかったが、なんとなく自分の中で結論が出たので忘れないように書いておくと、
ローカルだろうがインターネット上のサーバーで動作していようが、 client.run(TOKEN)
が実行されて動作している間はDiscordサーバーに接続されて、オンラインと認識される。ということだと思う。
ソースコードを読んでないし確実な情報ではないが、多分そんなところだと思う。
heroku周りの準備
CLIからherokuにログインする。
heroku login
ブラウザでherokuにログインする。
Heroku 上の Container Registry へログインする。
heroku container:login
DiscordのTOKENを設定する。
heroku config:set TOKEN=hogehoge
Dockerの設定
FROM python:3.7-slim
ENV HOME=/app
WORKDIR /app
# NEologdのインストールに使う
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
openssl \
sudo \
zip \
file
# MeCabのインストール
RUN apt-get update && apt-get install -y \
mecab \
libmecab-dev \
mecab-ipadic \
mecab-ipadic-utf8
RUN cd /usr/share/mecab && \
git clone https://github.com/neologd/mecab-ipadic-neologd.git && \
cd mecab-ipadic-neologd/ && \
./bin/install-mecab-ipadic-neologd -n -a -y -p /usr/share/mecab/dic/mecab-ipadic-neologd/
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
COPY . .
# discord_bot.py を実行してbotを起動する
CMD ["python", "discord_bot.py"]
使用ライブラリの設定
mecab-python3
discord.py
botの実装
コマンドは使わず、全てのメッセージから、
- メッセージの文字列の先頭が "mecab " で開始されていたら
- スペース区切りで分割して
- 末尾の文字列を形態素解析対象にして
- 間の文字列はMeCabのオプションとして
- 形態素解析した結果をメッセージで送る
import os
import MeCab
import discord
TOKEN = os.environ["TOKEN"]
PREFIX = "mecab "
client = discord.Client()
@client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
@client.event
async def on_message(message):
print("received message: " + str(message))
if message.content.startswith(PREFIX):
# 送り主がBotだった場合反応しない
if client.user != message.author:
print(message.content)
splited_message = message.content.split() # スペースで分割する
splited_message.pop(0) # 先頭は "mecab" なので不要
content = splited_message.pop() # 末尾は解析対象の文字列として扱う
option = ' '.join(splited_message) # "mecab " から "{対象文字列}" の間の文字列をスペースで連結する
mecab = MeCab.Tagger(option)
m = "```" + mecab.parse(content) + "```"
print(m)
# メッセージが送られてきたチャンネルへメッセージを送る
await message.channel.send(m)
client.run(TOKEN)
herokuにデプロイ
heroku container:push web --app discord-bot-sample-app
heroku container:release web --app discord-bot-sample-app
デプロイが成功したら、DiscordのdeveloperページのOAuth2のページの SCOPES にチェックボックスを入れて下部に表示されるURLにアクセスして、サーバーにbotを接続する。
これでbotが動く(はず)。
使い方
mecab こちらの公開リポジトリに動作環境を反映してあります。
とメッセージすると、
こちら 名詞,代名詞,一般,*,*,*,こちら,コチラ,コチラ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
公開 名詞,サ変接続,*,*,*,*,公開,コウカイ,コーカイ
リポジトリ 名詞,一般,*,*,*,*,*
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
動作 名詞,サ変接続,*,*,*,*,動作,ドウサ,ドーサ
環境 名詞,一般,*,*,*,*,環境,カンキョウ,カンキョー
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
反映 名詞,サ変接続,*,*,*,*,反映,ハンエイ,ハンエイ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
あり 動詞,非自立,*,*,五段・ラ行,連用形,ある,アリ,アリ
ます 助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS
こんな返事がかえってくる。
mecab -d /usr/share/mecab/dic/mecab-ipadic-neologd/ まずは対象となる文字列を準備しましょう。
というふうに NEologd の辞書を指定すると、
こちら 名詞,代名詞,一般,*,*,*,こちら,コチラ,コチラ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
公開 名詞,サ変接続,*,*,*,*,公開,コウカイ,コーカイ
リポジトリ 名詞,固有名詞,一般,*,*,*,リポジトリ,リポジトリ,リポジトリ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
動作環境 名詞,固有名詞,一般,*,*,*,動作環境,ドウサカンキョウ,ドーサカンキョー
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
反映 名詞,サ変接続,*,*,*,*,反映,ハンエイ,ハンエイ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
あり 動詞,非自立,*,*,五段・ラ行,連用形,ある,アリ,アリ
ます 助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS
動作環境
のところだけが変わっているが、ちゃんとNEologdの辞書を使っているようだ。
問題点
この実装だと、botが接続しているサーバーのメッセージを全て受信して、mecab
の文字列が存在するかをチェックしてしまう(んだと思う。多分)。
ってことは大半の関係のないメッセージを処理するコストがかかってしまい無駄が多い。
コマンドを実装すれば、prefix文字列とコマンド文字列が存在したときだけ、メッセージを受信することができる(はず。多分)。
コマンド版の実装
import os
import MeCab
import discord
from discord.ext import commands
TOKEN = os.environ["TOKEN"]
bot = commands.Bot(command_prefix='!', description='Output to results of morphological analysis.')
@bot.event
async def on_ready():
print('Logged in as')
print(bot.user.name)
print(bot.user.id)
print('------')
@bot.command()
async def mecab(ctx, *args):
print("received message: " + str(args))
if bot.user != ctx.message.author:
l = list(args)
content = l.pop() # 末尾は解析対象の文字列として扱う
print(content)
option = ' '.join(l)
mecab = MeCab.Tagger(option)
m = "```" + mecab.parse(content) + "```"
print(m)
# メッセージが送られてきたチャンネルへメッセージを送ります
await ctx.send(m)
bot.run(TOKEN)
非command版との違いは、
-
client (discord.Client)
がbot (commands.Bot)
になっている。 -
mecab
メソッドでcommandを受信する。 - 受け取る引数はMessageではなく、
args
のタプルになる。
Dockerfileを書き換えてデプロイ
コマンド版で実行するようにDockerfileを書き換える。
CMD ["python", "discord_commmand_bot.py"]
herokuにデプロイする。
heroku container:push web --app discord-bot-sample-app
heroku container:release web --app discord-bot-sample-app
これで !mecab
コマンドが有効になったはず。
実験
!mecab こちらの公開リポジトリに動作環境を反映してあります。
こちら 名詞,代名詞,一般,*,*,*,こちら,コチラ,コチラ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
公開 名詞,サ変接続,*,*,*,*,公開,コウカイ,コーカイ
リポジトリ 名詞,一般,*,*,*,*,*
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
動作 名詞,サ変接続,*,*,*,*,動作,ドウサ,ドーサ
環境 名詞,一般,*,*,*,*,環境,カンキョウ,カンキョー
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
反映 名詞,サ変接続,*,*,*,*,反映,ハンエイ,ハンエイ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
あり 動詞,非自立,*,*,五段・ラ行,連用形,ある,アリ,アリ
ます 助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS
mecab こちらの公開リポジトリに動作環境を反映してあります。
反応なし。
成功。
追記
botがすぐオフラインになってしまう
デプロイして数分はbotとして機能しているが、すぐにオフラインになってしまう。
またデプロイしたりdynoをON/OFFすれば復帰するが、常時動かしておきたい。
調べてみると、デプロイ時に web
としてcontainerをpushしていたのが良くなかったみたい。
heroku container:push web --app discord-bot-sample-app
heroku container:release web --app discord-bot-sample-app
workerとしてデプロイし直す。
heroku container:push worker --app discord-bot-sample-app
heroku container:release worker --app discord-bot-sample-app
確かにこれで止まらなくなった。