Amazon Translateを使ってSlackで翻訳できるようにしてみた

こんにちは。@j_nakayamaです。

#つくった背景
よく仕事の息抜きに業務が効率化するようなbotや新しい技術を遊びながら使ってみたりしてます。
今回もそんな延長線上でまだ試していなかったAmazon TranslateのAPIを使用してSlackとの連携機能を作ってみました。
Slackでの会話の中で英語記事の引用が時々あります。
その際、Google翻訳を開いたりするのが面倒だったのでSlack完結で翻訳できるようにしました。

# つくるもの
SlackのSlash Commandsを使用し、入力した文章を翻訳して結果をSlackチャンネルへ返します。
今回は投稿された文字が日本語か英語かを判断してどちらにも変換できるようにします。

例
Slackの投稿フォームに入力
`/translate Hello`
↓
指定チャンネルへ翻訳した結果が返される
`こんにちは`

#システム概要
言語 ： Python3.6
![aws translate_slack連携.png](https://qiita-image-store.s3.amazonaws.com/0/311029/db9492cf-7e63-ece1-b685-0515371431c9.png)

#手順
1. [AWS] IAMロール作成
2. [AWS] Lambda関数の作成
3. [AWS] API Gatewayの作成
4. [Slack] Slack APIのtoken作成
5. [Slack] Slash Commandsの作成
6. [AWS] Lambda関数の環境変数を設定

#さっそく作ってみる


### 1. IAMロール作成
まずはLambdaからAmazon Translateへのアクセス権限を付与するための設定を行います。
また、CloudWatchへログも確認したいのでCloudWatchLogsのアクセス権限も付与します。

1-1. awsのマネージメントコンソールから **[サービス] > [IAM] > 左ペイン[ロール]**  を選択
1-2. 「ロールの作成」ボタンをクリック
1-3. 「このロールを使用するサービスを選択」でLambdaを選択し「次のステップ：アクセス権限」ボタンをクリック
![IAM1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/701f246e-08cf-7913-b383-dc3c676a7aa8.png)
1-4. 「Attachアクセス権限ポリシー」で下記のポリシーを選択し「次のステップ：タグ」ボタンをクリック

- TranslateReadOnly
- CloudWatchLogsFullAccess

![IAM2.png](https://qiita-image-store.s3.amazonaws.com/0/311029/8a327b6a-926c-11fe-22c5-1d4bc6324a97.png)
1-5. 「タグの追加」はそのままで「次のステップ：確認」ボタンをクリック
1-6. ロール名を入力し「ロールの作成」ボタンをクリック
　　このときポリシーに1-4で選択したものが入っていればOK
![IAM3.png](https://qiita-image-store.s3.amazonaws.com/0/311029/fc06ab38-68de-8198-a22a-39a557541c1e.png)
　　ロール名 : 任意（ここでは「translate_test」で設定）

### 2. Lambda関数の作成

Amazon Translateの対応リージョンは今現在、下記の5地点のみのため今回は **「米国東部（北バージニア）」** リージョンで作成します。

- 米国東部（バージニア北部）
- 米国東部（オハイオ）
- 米国西部（オレゴン）
- GovCloud (米国西部)
- EU（アイルランド）

2-1. awsのマネージメントコンソールから **[サービス] > [Lambda] > 「関数の作成」**  を選択
2-2. 「一から作成」を選択し、「名前」などを入力
![lambda1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/842fc3a1-c15b-7c8c-1f4c-5ec9dea6faa5.png)
　　名前 ： 任意（ここでは「translate」で設定）
　　ランタイム ： Python3.6
　　ロール : 既存のロールを選択
　　既存のロール : 手順「1. IAMロール作成」で作成したロールを選択

2-3. 「関数の作成」ボタンクリック
2-4. 関数作成の画面にて下記コードをインラインにて入力し、「保存」ボタンで保存

```python:lambda_handler.py
# coding: UTF-8
import boto3
import json
import logging
import os
import urllib
import unicodedata

ENCRYPTED_EXPECTED_TOKEN = os.environ['kmsEncryptedToken']
SLACK_TOKEN = os.environ['slackToken']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    params = urllib.parse.parse_qs(event['body'])
    token = params['token'][0]

    # トークン確認
    if token != ENCRYPTED_EXPECTED_TOKEN:
        logger.error("Request token (%s) does not match", token)

    if params.get('text') is None:
        logger.error("Request text undefine!!!!!")
        return {
            'text': "翻訳したい文字を入力してください"
        }

    return respond(params, None)

def respond(params, err, res=None):
    message = params['text'][0]
    channel = params['channel_id'][0]

    # 日本語の場合、英語に変換
    if is_ja(message):
        SourceCode='ja'
        TargetCode='en'
    else :
        SourceCode='en'
        TargetCode='ja'

    # 翻訳
    translate = boto3.client('translate')
    response = translate.translate_text(
        Text=message,
        SourceLanguageCode=SourceCode,
        TargetLanguageCode=TargetCode
    )

    # Slackへpost
    slack_message(channel, response['TranslatedText'])

    # Slash Commandsへreturn
    return {
        'text': "Input: " + message
    }

def is_ja(string):
    for char in string:
        name = unicodedata.name(char) 
        if "CJK UNIFIED" in name or "HIRAGANA" in name or "KATAKANA" in name:
            return True
    return False

def slack_message(channel, message):

    url = 'https://slack.com/api/chat.postMessage'
    headers = {
        'Authorization': 'Bearer ' + SLACK_TOKEN,
        'Content-Type': 'application/json; charset=utf-8'
    }
    method = 'POST'
    data = {
        "channel": channel,
        'text': message,
        'username': 'translate',
        'icon_emoji': ':neko_02:',
        'link_names': 1,
    }

    req = urllib.request.Request(url, json.dumps(data).encode(), headers, method)
    res = urllib.request.urlopen(req, timeout=5)

    return
```

※ この時点では環境変数から取得する「kmsEncryptedToken」と「slackToken」が設定されていないのでまだ動きません。こちらは最後に設定します。

### 3. API Gatewayの作成
ここではAPI Gatewayを設定し、API Gatewayから先程作成したLambda関数を起動するように設定します。

3-1. Lambda関数を設定した画面内でトリガーを追加
リストから「API Gateway」をクリック
![lambda_APIgateway1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/64ee3004-1328-776f-2aec-a237c159f827.png)
↓
トリガーにセットされ、「トリガーの設定」も合わせて表示される
![lambda_APIgateway2.png](https://qiita-image-store.s3.amazonaws.com/0/311029/e89cdd16-aef2-4a3a-56f7-cc1722c0cf03.png)

3-2. 「トリガーの設定」に下記入力し「追加」ボタンクリック
　　API : 新規APIの作成
　　セキュリティ : オープン
　　（▼追加の設定クリック）
　　API名 : 任意（ここでは「translate」で設定）
　　デプロイされるステージ : 任意（ここでは「prod」で設定）

3-3. このままでは保存されていないのでLambdaの「保存」ボタンもクリック
3-4. 保存されると「トリガー設定」の表示が変わるのでAPI名（translate-API）でをクリック
3-5. API Gatewayのコンソール画面に移動するので「統合リクエスト」をクリック
![APIGateway1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/4cb0aa1e-d878-5c14-9a06-6ebd541b863d.png)
  Lambda プロキシ統合の使用 : チェックをはずす
  それ以外はデフォルトのまま

3-6. 「マッピングテンプレート」をクリックし、下記を入力し保存
リクエスト本文のパススルー : テンプレートが定義されていない場合（推奨）

　　Content-Type　：　application/x-www-form-urlencoded
　　テンプレート　：　`{ “body”: $util.urlDecode($input.json(“$”)) }`
![APIGateway2.png](https://qiita-image-store.s3.amazonaws.com/0/311029/618ebedd-dae3-08a6-c975-04af0e3ec406.png)

3-7. APIをデプロイする
リソース横の「アクション」ボタン > 「APIのデプロイ」
  デプロイされるステージ : 任意（ここでは「prod」）
その後、「デプロイ」ボタンクリック
デプロイされるとAPI GatewayのエンドポイントURLが表示されるのでメモしておく

エンドポイントURLはこんな感じ↓
https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/prod/translate


### 4. Slack APIのtoken作成
翻訳された結果をSlackへ返すためSlack APIでtokenを生成します。
このトークンを指定することでアプリ内でSlack messageの送信が可能になります。

4-1. Slack API（ https://api.slack.com/ ) へアクセス
![slack_token.png](https://qiita-image-store.s3.amazonaws.com/0/50160/50ed92ce-974b-ff72-1ae2-00ae7300cb7c.png)
4-2. 「Start Building」ボタンクリック
4-3. Create a Slack Appの画面で下記を入力し「Create App」ボタンをクリック
![slack_token1.png](https://qiita-image-store.s3.amazonaws.com/0/50160/c041c033-5958-53bd-5325-7c5ebe102884.png)
　　App Name : 任意（ここではtranslate）
　　Development Slack Workspace : 任意（postしたいSlack名）

4-4. Building Apps for Slack内 の Add features and functionalityから「Permissions」ボタンをクリック
![slack_token6.png](https://qiita-image-store.s3.amazonaws.com/0/50160/f53d267e-a406-5ae5-8185-a100342df36a.png)
4-5. [Scopes] のプルダウン内で下記を選択し「Save Changes」ボタンをクリック
![slack_token2.png](https://qiita-image-store.s3.amazonaws.com/0/50160/dd51e62f-c3f8-da21-01ba-23465910c6a8.png)

- Send messages as test (chat:write:bot)
- Send messages as user (chat:write:user)

4-6. 同ページ内一番上の「Install App to Workspace」ボタンがアクティブになるのでクリック
![slack_token3.png](https://qiita-image-store.s3.amazonaws.com/0/50160/f365ade1-6756-caee-1c08-f2963bb14d55.png)
4-7. アプリとSlackの連携画面が出てくるので「Authorize」ボタンをクリック
![slack_token4.png](https://qiita-image-store.s3.amazonaws.com/0/50160/9c6fdbd4-ad41-790d-4363-bd71f83159c3.png)
4-8. OAuth Access Tokenに表示される値をメモしておく
![slack_token5.png](https://qiita-image-store.s3.amazonaws.com/0/50160/e19a4999-5277-b724-616a-834b55f15f08.png)

### 5. Slash Commandsの作成
翻訳したい文章や単語を「/translate xxxxx」のような形式でSlackに入力するための設定を行います。

5-1. Slackの管理画面 **[Custom Integrations] > [Slash Commands]**
ここでは「/translate xxxxx」で入力させたいのでChoose a Commandに「/translate」と入力
![slash_commands1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/1380bcf1-2099-dd33-f6bc-25a9b62552d8.png)
5-2. Slash Commandsの詳細設定を行う
![slash_commands2.png](https://qiita-image-store.s3.amazonaws.com/0/311029/583640a5-2bc6-4c47-2891-eaf8fa8a8a81.png)
下記を入力し「Save Settings」ボタンクリック

　　URL : 3-7でメモしたエンドポイントURL
　　それ以外はデフォルトのまま
　　Tokenの値はメモしておく

### 6. Lambda関数の環境変数を設定
Lambdaの設定で残っていた環境変数を設定します。

6-1. awsのマネージメントコンソールから **[サービス] > [Lambda]**  を選択
6-2. [2. Lambda関数の作成]で作成した関数をクリック
6-3. 関数の設定画面の「環境変数」に下記を入力し「保存」ボタンクリック
![lambda2.png](https://qiita-image-store.s3.amazonaws.com/0/50160/c3de5715-eb89-74ff-cb59-c18b805a9971.png)

　　slackToken : 4-8でメモしたURL
　　kmsEncryptedToken : 5-2でメモしたtoken

# 動かしてみる

`/translate Hello`を入力
![translate1.png](https://qiita-image-store.s3.amazonaws.com/0/311029/48dc1799-361d-01ce-834d-7c347a38a3ca.png)

`/translate こんにちは`を入力
![translate2.png](https://qiita-image-store.s3.amazonaws.com/0/311029/98d537e8-d253-c7c6-6638-8d58c8b58b4e.png)

`/translate The weather is good today, is not it`を入力
![translate3.png](https://qiita-image-store.s3.amazonaws.com/0/311029/0c8ee693-acad-5d91-64c4-bb0e285e825a.png)


`/translate 今日はいい天気ですね`を入力
![translate4.png](https://qiita-image-store.s3.amazonaws.com/0/311029/b2ef50d4-f379-caaa-66ef-f7528ca518d7.png)




# まとめ
Amazon Translateで日本語が使えるようになって半年くらい経過してしまい、今更ながらに触ってみましたがとても簡単に使えました。
日本語→英語はまだマシですが、英語→日本語の精度が気になります。
機械学習が主流ですし、もう少し精度が上がってくるだろうと期待してます。