- 概要
- 処理の流れ
- 環境
- 下準備
- discordBotのテスト起動
- pythonからamazon translateを動かす
- discordBotに翻訳処理を追加する
- 実際に動かす
- 感想
- 改善点
- 参考文献
概要
discord(slackのようなコミュニケーションツール)のbotをec2上で動かす。botがコマンドに反応して、amazon translate経由で翻訳を実現する。ec2上で動かすことで、botが常駐して翻訳できる。
処理の流れ
- discordでユーザーがコマンドを入力する
- botがコマンドに反応し、翻訳先言語と文章を取得する
- amazon translateに翻訳先言語と文章を渡して翻訳済み文章を取得する
- botがコマンドに翻訳済み文章を返信する
環境
- mfa有効化IAMユーザー
- ec2 Amazon Linux 2023
- Python 3.9.16
- boto3==1.29.0
- discord.py==2.3.2
- python-dotenv==1.0.0
下準備
botを動かす前の下準備として、
以上の設定を行う。
ec2の作成
新しくキーペアを作成 Users/user/.sshとかに保存しておく。
ログインキーペアに作成したキーペアを設定。
パブリック IP 有効化、セキュリティグループはsshを許可する。
ec2にssh接続
vscodeでssh接続するので上記の記事通り設定していく。 Users/username/.ssh/configに以下の内容を書き込む。
Host discord-ec2 HostName <ec2のip> User ec2-user Port 22 UserKnownHostsFile /dev/null PreferredAuthentications publickey StrictHostKeyChecking no PasswordAuthentication no IdentityFile /Users/<username>/.ssh/<保存したキーペア>.pem IdentitiesOnly yes LogLevel FATAL
ec2にpython,pip,必要ライブラリのインストール
terminalで
sudo yum update sudo yum install python3 sudo yum install pip pip install discord pip install python-dotenv
を順番に実行。
python3 -V pip freeze
でちゃんとインストールできたか確認する。
mkdir test cd test
適当な作業ディレクトリも作っておく。
discordBotの作成
Botをサーバーに入れるところまでは上記の記事通り設定していく。
discord dev portalからNew Applicationsでbotを新規作成した後は、
Botタブのreset tokenでbotのtokenをコピーしておく。
intentsの設定でMESSAGE CONTENT INTENTをオンにしておかないと、コマンドの取得ができないのでオンにしておく。
OAuth2のURL GeneratorのScopesで、botをチェック
同画面の下にあるBot Permissionsで、Send MessagesとRead Message HistoryをチェックしてGENERATED URLをコピー
管理者権限持ってるサーバーを選択し、botを追加
これでbotが追加できた。 ec2の作業ディレクトリに.envファイルを作成し、
DISCORD_TOKEN=<コピーしたtoken>
と書き込んでおく。
aws cliの設定
aws consoleでIAMにアクセスし、使用するユーザーのアクセスキーを作成する。 アクセスキーとシークレットアクセスキーをコピーしておく。
aws configure
で指示に従いアクセスキーとシークレットアクセスキーを入力する。
MFAの一時的認証
MFAを有効化している場合、aws configureだけだとamazon translateの権限が足りず、pythonから接続ができない。 そこで、一時的なMFA認証をする。
上記の記事通り設定していく。
aws sts get-session-token \ --serial-number <mfaデバイスのarn> \ --token-code <token code>\ --profile mfa
このコマンドでAccessKeyId,SecretAccessKey,SessionTokenが得られるのでコピーしておく。
export AWS_ACCESS_KEY_ID=<AccessKeyId> export AWS_SECRET_ACCESS_KEY=<SecretAccessKey> export AWS_SESSION_TOKEN=<SessionToken>
環境変数に保存しておく。
これでひとまず下準備終わり。
discordBotのテスト起動
discordBotのテストとして、!greetコマンドに対して"hello"と返信するBotを作成してみる。
import discord from dotenv import load_dotenv from discord.ext import commands import os load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') # Intentsを作成 intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents, help_command=None) @bot.event async def on_ready(): print('on ready') @commands.command() async def greet(ctx): await ctx.reply("hello") bot.add_command(greet) bot.run(TOKEN)
動作確認ができた。
コード解説
load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') # Intentsを作成 intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents, help_command=None)
以上の設定をしている。
@bot.event async def on_ready(): print('on ready') @commands.command() async def greet(ctx): await ctx.reply("hello") bot.add_command(greet) bot.run(TOKEN)
- bot起動時にprint出力
- greetコマンドの登録
- コマンドの追加
以上の設定をしている。
pythonからamazon translateを動かす
Translate.py
import boto3 class Translate: def __init__(self, region_name='ap-northeast-1'): self.translate_client = boto3.client('translate', region_name=region_name) def translate_text(self, text, target_language_code): response = self.translate_client.translate_text( Text=text, SourceLanguageCode="auto", TargetLanguageCode=target_language_code ) return response["TranslatedText"] t = Translate() # テキストを翻訳 response = t.translate_text( text="こんにちは", target_language_code="en" ) print(response)
あとで使い回すのでクラス化してある。Translate.pyで保存しておく。
よさそうです。
コード解説
response = t.translate_text( text="こんにちは", target_language_code="en" )
textとtarget_language_codeを変更すると応じた結果に変わる。
def translate_text(self, text, target_language_code): response = self.translate_client.translate_text( Text=text, SourceLanguageCode="auto", TargetLanguageCode=target_language_code ) return response["TranslatedText"]
responseから翻訳済み文章だけを抽出する。SourceLanguageCodeをautoにすると、comprehendで言語推定をして補完してくれる。しかし、その分の使用料がかかるので注意。
discordBotに翻訳処理を追加する
テストとして作成した、!greetコマンドに対して"hello"と返信するBotをベースに、翻訳処理を追加する。
main.py
import discord from dotenv import load_dotenv from discord.ext import commands import os import Translate load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') # Intentsを作成 intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents, help_command=None) language_codes = [ "af", "sq", "am", "ar", "hy", "az", "bn", "bs", "bg", "ca", "zh", "zh-TW", "hr", "cs", "da", "fa-AF", "nl", "en", "et", "fa", "tl", "fi", "fr", "fr-CA", "ka", "de", "el", "gu", "ht", "ha", "he", "hi", "hu", "is", "id", "ga", "it", "ja", "kn", "kk", "ko", "lv", "lt", "mk", "ms", "ml", "mt", "mr", "mn", "no", "ps", "pl", "pt", "pt-PT", "pa", "ro", "ru", "sr", "si", "sk", "sl", "so", "es", "es-MX", "sw", "sv", "ta", "te", "th", "tr", "uk", "ur", "uz", "vi", "cy" ] # translateのインスタンス作成 t = Translate.Translate() @bot.event async def on_ready(): print('on ready') @commands.command() async def help(ctx): await send_help(ctx) @commands.command() async def tl(ctx, *args): print(f"args:{args}") # 引数が2個未満のときはhelpメッセージ表示してreturn if(len(args) < 2): await send_help(ctx) return language_code = args[0] text = args[1] # 最初の引数が言語コードじゃない場合はhelpメッセージ表示してreturn if(language_code not in language_codes): print(f"ng {language_code}") await send_help(ctx) return print(f"言語コード:{language_code}\nテキスト:{text}") # テキストを翻訳 response = t.translate_text( text=text, target_language_code=language_code ) await ctx.reply(f"{response}") async def send_help(ctx): msg = """ 翻訳コマンド !tl 例:!tl [language_code] [text] 指定した言語に文章を翻訳します [language_code] 言語コード 対応コード一覧:https://docs.aws.amazon.com/ja_jp/translate/latest/dg/what-is-languages.html [text] 翻訳する文章 スペースが含まれる場合はダブルクォーテーション("")で囲う """ await ctx.send(msg) bot.add_command(tl) bot.add_command(help) bot.run(TOKEN)
テスト用のdiscordBotに
この3つの処理を追加した。
tlコマンドが実行されたら以下の順番で処理をする。
- コマンドの引数が2個未満なら、helpメッセージを表示してreturn
- 1つ目をlanguage_code,2つ目をtextとして代入する
- language_codeがamazon translateの対象リスト(language_codes)に含まれていない場合は、helpメッセージを表示してreturn
- language_codeとtextをTranslate.translate_text()に渡し、翻訳済み文章を取得
ctx.reply()
で元コマンドに返信する
実際に動かす
helpコマンドもtlコマンドも問題なく動いてますね。
感想
1日でシステム完成までできて簡単にアプリを作れたのは、サーバーの諸設定について考慮しなくていいawsならではだと思う。 今回は常駐型でサーバー有りでbotを動かしたが、lambdaでサーバーレス構成にすることも可能なので、拡張性が高い。 詰まったところは、
この3つだった。特に、intents設定は新しく追加された設定で、昔個人で使用した際にはなかったため調べるのに時間がかかった。ほか2つは調べるとすぐ解決できた。
改善点
- lambda+INTERACTIONS ENDPOINT URLで完全サーバーレス化
- スラッシュコマンドを使ってUIをわかりやすくする
- 翻訳前文章の言語推定をcomprehend経由ではなく、他のサービスにして言語推定にかかる料金を節約
- awsの他のサービスと連携して機能拡張
上記の改善点が挙げられる。INTERACTIONS ENDPOINT URLでのサーバーレスは以前に一回試したが、新しい機能で参考にする資料が少なく、うまくいかなかったた。