ノムリッシュ翻訳してくれるSlack bot(AWS lambda + APIGateway)

# はじめに
ノムリッシュ翻訳とは日本語の文章を野村ファイナルファンタジー風用語（意識高い系）に変換してくれるWebサービスです。
[ノムリッシュ翻訳 -ファビュラ・ノヴァ・ノムリッシュ](https://racing-lagoon.info/nomu/translate.php)

例えば、
> 私の好きな食べ物はお寿司です。

をノムリッシュ翻訳すると、
> 私の好きな食べ物は指揮官専用機として開発したウォ・スシです。
そしてその手には　それぞれクリスタルが握られていた・・・

のように面白おかしく変換してくれます。

また、こちらの機能がnpmモジュールとして公開されているので、こちらを使ってSlackでノムリッシュ翻訳してくれるSlack botを作ろうと思い、実際に作ってみました。
[npm nomlish](https://www.npmjs.com/package/nomlish)
[ノムリッシュ翻訳ライブラリをnpmに公開しました](https://qiita.com/SugarShootingStar/items/180b36ac35ef7ab2e2aa)

# 完成イメージ
"nomlish 文章"と投稿すると、文章を翻訳してくれます。
![Slack_bot_img.png](https://qiita-image-store.s3.amazonaws.com/0/203706/e4f2f83e-93d0-d898-8412-29fb90ac7496.png)


# 構成
lambda + APIGatewayを使用して、APIを作成し、Slackから作成したAPIをコールしレスポンスを受け取ります。
![Slack_bot_architecture.png](https://qiita-image-store.s3.amazonaws.com/0/203706/402af3f4-98f6-fb4d-4ffe-30450e2240f9.png)


# 準備
* Slack アカウント
* AWSアカウント
* Node.js(npmコマンド) が動作する環境

# 構築手順
## コード
lambdaで実行させるコードを準備します。
1. ```npm init```コマンドで、Node.jsプロジェクトを作成
2. 先ほど紹介したノムリッシュモジュールを```npm i nomlish```コマンドでインストール
3. プロジェクト直下にindex.jsを作成(下記参照)
4. index.jsとnode_modulesフォルダをまとめてZip化

```javascript:index.js

var nomlish = require("nomlish");
exports.handler = (event, context, callback) => {
  console.log(event.text);
  var planeText = event.text.replace("nomlish ", "");
  var beforeText = planeText.replace(/\r?\n/g, '');
  var res = {};
  res.username = "ノムリッシュ";
  nomlish.translate(beforeText, 3).then(function(nomlishText) {
    res.text = JSON.stringify(nomlishText);
    callback(null, res);
  })
};
```

こちらはリクエストから渡ってくる文章を成形し、nomlish.translate()で翻訳し、結果をJSONに直してレスポンスを返す処理になります。


## lambda
準備したコードを動作させる環境を作成します。AWS lambdaは関数単位で実行できるサーバレスのフルマネージドサービスです。
1. AWSコンソールにアクセスしてLambdaサービスを開く
2. 一から関数を作成（デフォルトのまま特に細かい設定は必要ありません）
3. 作成完了後、関数コード内、コードエントリタイプ「.zipファイルをアップロード」を選択し、先ほど作成したzipファイルをアップロード
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/333f2ca0-f9dd-e5c0-53ee-3b1d8f0e096b.png)

これでlambdaの準備が完了です。

## APIGateway
先ほど作成したlambdaとAPIGatewayを使って、SlackからコールされるAPIを作成します。Amazon API GatewayはAPIの作成、管理ができるフルマネージドなサービスです。
1. AWSコンソールにアクセスしてAPI Gatewayサービスを開く
2. APIの作成（デフォルトのまま特に細かい設定は必要ありません）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/bb8a7a15-dfcc-67d3-0ca5-8d6637976fcc.png)
3. 「アクション」>「メソッドの作成」を選択し、プルダウンから「POST」を選択する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/3e3f46f5-2f52-122d-7619-9ddc4e7fa5d3.png)
4. -POST-セットアップの画面で、先ほど作成したLambda関数名を入力する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/c9b1a177-44d8-7241-23ac-0a35cc3ee986.png)
5. 「統合リクエスト」を選択し、マッピングテンプレートを開き、「リクエストの Content-Type ヘッダーに一致するテンプレートがない場合」を選択する。さらに、Content-Typeに「application/x-www-form-urlencoded」を入力し、下記コードを追加する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/41cd455f-49ca-8d9b-8dde-438746c69ce8.png)



```
## convert HTML POST data or HTTP GET query string to JSON

## get the raw post data from the AWS built-in variable and give it a nicer name
#if ($context.httpMethod == "POST")
 #set($rawAPIData = $input.path('$'))
#elseif ($context.httpMethod == "GET")
 #set($rawAPIData = $input.params().querystring)
 #set($rawAPIData = $rawAPIData.toString())
 #set($rawAPIDataLength = $rawAPIData.length() - 1)
 #set($rawAPIData = $rawAPIData.substring(1, $rawAPIDataLength))
 #set($rawAPIData = $rawAPIData.replace(", ", "&"))
#else
 #set($rawAPIData = "")
#end

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())

## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
 #set($rawPostData = $rawAPIData + "&")
#end

## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))

## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])

## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end

## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
  ## finally we output the JSON for this pair and append a comma if this isn't the last pair
  #set($kvTokenised = $kvPair.split("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}
```
設定が完了したら、APIをデプロイします。
6. 「アクション」>「APIのデプロイ」を選択し、「デプロイされるステージ」に[新たなステージ]を選択する
7. 「ステージ名」に適当な名前を入力し、「デプロイ」ボタンを押下する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/37cf8542-4602-d935-75cc-498e7d055957.png)
8. デプロイが完了し、ステージエディター内にURLが表示される
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/bff19759-f4cd-fecc-58cb-7b3a09f153a4.png)

以上でAPIGatewayの設定は完了です。

## Slack
作成したAPIをSlackからコールする設定を行います。Outgoing Webhooksを使用するのですが、こちらは設定した言葉がトリガーとなって、SlackからPOSTのメソッドを送信し、指定した形式でJSONが返されると投稿してくれるものになります。
1. サイドバーの「Apps」を押下、App Directory画面内の検索窓に「Outgoing WebHooks」と入力しOutgoing WebHooksの設定画面を開く
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/43964835-2269-bfee-bc7a-f5777c57476b.png)
2. 「Add Configuration」を押して、今回作成する設定を追加する
3. 各種設定する（今回使用するもののみ説明）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/67b94c61-f36c-626a-f4f5-25b13476f48e.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/203706/f7077ec5-2295-15eb-11ed-55c375f70d68.png)
Chanel：適用するチャンネルの選択
Trigger Words(s)：トリガーとなる言葉の設定、今回はnomulishを指定
URL(s)：API URLの設定、先ほどAPIGatewayで作成したURLを指定
Customize Name：botが投稿する際の名前、ただし今回はAPIのレスポンスで名前を指定
Customeize Icon：アイコンの指定
4. 以上設定出来たら、「Save Settings」を押下、完成です！

# 雑談
実は当初、API部をGASで作成する予定でした。が、ノムリッシュのモジュールをバンドルするのに苦労して、今回の構成で作成することを決めました。こちらを応用すれば、天気を教えてくれるbotなども作れちゃいます！