Slack、Watson Assistant、IBM Functions、Db2 on Cloudの連携メモ

Watson AssistantとDb2 on Cloudの連携でいろいろと手間取ったので、ログとして実施した手順を記載しておきます。SlackとWatson Assistantの連携は、Watson Assistantの手順に従えば簡単に連携できるようになっています。

###どんな感じの処理なのか？
Slackに入力した値をWatson Assistantで受けて、IBM FunctionsからSQLでDb2 on Cloudの情報を持ってきます。そしてWatson Assistantで回答した内容を再度Db2 on Cloudに書き込むという内容です。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/290e71c6-7f20-bdc3-c61c-d82c0d6f6501.png)

###参考文献
↓Watson AssistantとDb2を外部連係する
https://qiita.com/schiyoda/items/89064693d6353fdc69e1


###前提  
- IBM CloudでDb2 on Cloud, Functions, Watson Assistant, ワークスペース（Cloud Foundryベース）を作成する手順は省略
- Slackの対象ワークスペースに管理者権限を持っていること
- IBM Cloudのアカウント（ライトアカウント）を持っていること
- Watson assistantのSkillは作成してあること
- Db2 on Cloudのデータベースとテーブルは作成してあること

---
# Db2 on CloudのTable構成  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/46c23179-04e1-d1a1-64ca-a9618af4bab7.png)  
Db2 on Cloudのデータを取り込んでWatson Assistantに回答し、またその戻り値をDb2 on Cloudに保存するためのテーブルを用意する。
今回は、以下の構成でDb2 on CloudのTableを構成する
**Watson Assistantに回答するテーブル**

- DB名：MPX09999
- Table名：TABLE01
- Column：name , VARCHAR(255)
- Column：content , VARCHAR(255)
- Column：Link , VARCHAR(255)
- Column：remark , VARCHAR(255)  

**Watson Assistantにログを保存するテーブル**

- DB名：MPX09999 
- Table名：LOG
- Column： result1, VARCHAR(255)
- Column： result2, VARCHAR(255)
- Column： result3, VARCHAR(255)
- Column： result4, VARCHAR(255) 
- Column： KEYWORD_INPUT, VARCHAR(255) 
- Column： KEYWORD_INTENTS, VARCHAR(255) 
- Column： KEYWORD_ENTITIES, VARCHAR(255)  

---
#Watson AssistantとSlack連携
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/278d3eb5-102b-1c83-608a-ff1c705e1d92.png)
1. Watson AssistantのCreate assistantを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/1844b7b5-5707-5274-5b89-d3415ec43c07.png)
2. Nameに任意の名前を入れて、Create assistantを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/b45ddd87-e1ec-e472-79cd-3057812f6c81.png)
3. Add dialog skillを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/c5e79094-4397-918f-c53b-e3f40fe84aa5.png)
4. Slack連携させる対象Skillを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/7a96c324-b76d-94b5-294f-0bdb4ff806d3.png)
5. SkillsにDialogが追加されたことを確認する  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/7890ca8c-44dc-98e7-978b-ce8fe6a51a68.png)
6. Add Integrationを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/fe5d70b0-1193-de41-3a89-80db839c1953.png)
7. Slackを押す（知らないうちに連携できるアプリが増えてた。。。今度試してみよう）  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/2adc7b5f-b3c1-e74e-3964-f49cc314bcfa.png)  
（以下、Watson Assisatntに記載されている手順通りに実施すればいいのですが、一応記載しておきます）
8. Slack API（ https://api.slack.com/apps ）にアクセスする
9. Create New Appを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/095aaf13-54c4-d715-8b0c-62a2322702b2.png)
10. App Nameに任意の名前を入れて、Development Slack Workspaceに追加するワークスペースを選択して、Create Appを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/79bb23ae-4a50-6808-881b-6f86546928dc.png)
11. SlackのBasic InformationのVerification TokenをWatson AssistantのVerification Tokenにコピーする  
↓Slackの画面  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/16787c24-16ec-4554-adbb-b8300bb42785.png)  
↓Watsonの画面  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/93c64963-51ed-7537-7023-31cbeeaad1d4.png)
12. Basic informationでBotsを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/ea616e0a-1fc1-342a-2978-0fdb50079b03.png)
13. Add a Bot Userを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/7783d48d-251d-6fa4-e0cd-4472304d893a.png)
14. Add a Bot User → Save Changesを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/0058b6cb-c034-3dfb-d8b1-4bc0cf2422d5.png)　→　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/047e69e8-97d7-ed59-123c-abde96bc7305.png)
15. OAuth & Permissionsをクリックし、Install App to Workspaceを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/c1969653-de4a-3236-a315-f3a74fe2a56f.png)
16. SlackワークスペースとBotsの連携確認画面が出るので、Installを押す  
17. SlackのOAuth Access TokenとBot User OAuth Access TokenをWatson Assistantにコピーする  
↓Slackの画面  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/59d89cec-a39e-12a9-2b07-7f2a089d4fea.png)  
↓Watsonの画面  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/6532ed17-2ada-704e-bb35-83455cdfc24f.png)
18. Create request URLを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/2a44fcfe-5624-7e3d-4fe0-2ded4c8e1c8c.png)  
↓URL作成後  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/016f6da9-0880-3cd4-ef7f-7a48e5e592f4.png)
19. SlackのEvent SubscriptionsをOnにして、Request URLにWatson Assistantで取得したURLを入力する。正常に登録されると、Verifiedという文字が表示される  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/92c4a7c1-4769-55d7-c21f-7725a4b519aa.png)  
↓正常にURLが認証される  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/0f44b427-6777-b41d-dcc0-f30004322161.png)
20. Add Bot User Eventでmessage.imを選択する  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/69d9aa76-f04a-8536-486b-4745eeaf8f90.png)  
↓message.imが正常に追加されている  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/d1912665-f304-3325-de06-c75266723ad8.png)
21. Watson AssistantのSave Changesを押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/8a215401-97ad-a150-fc13-a58976a31074.png)
22. 正常に登録されると下記画面のようにSlackが追加される  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/7e020103-f5b5-2f87-7622-1391933ba1e7.png)
23. Slackにアプリを登録して、文字を入力するとWatson Assistantからレスポンスが戻ってくるようになる  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/deab653c-2f8d-c809-70cf-d4d730903f1d.png)

---
# Db2 on CloudとFunctionsとWatson Assistantの連携
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/786985d7-2418-5486-8b93-35e96ad17b8b.png)
Cloud Foundryの名前空間およびスペースを利用していますので、ご注意ください。（IAMの名前空間ではない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/dac507b7-51ed-86cc-5d46-bafeb72b4bf1.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/3d2e463d-c8c4-bec0-8151-f230f3452ed6.png)  
1. IBM CloudのFunctionsにアクセスして、作成の開始を押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/bc7ce5d0-9549-667a-fed3-84aed0248b89.png)
2. アクションの作成を押す  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/1f7cd994-41a2-2a40-c5d2-b41ad1b35936.png)
3. アクション名に任意の名前を入れて、ランタイムにPython3を選択して作成を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/4bcf7360-8f13-ff36-e150-b9e97ad675aa.png)
4. Watson AssistantからFunctionsに連携されてDB2 on CloudにSQL Queryを実行するPythonスクリプト
Watson Assistantのname引数から文字を読み込んできて、それのSelect文の結果をリターンで戻している。  

```python:hogehoge-applicationの中身：MPX09999.TABLE01テーブルにSQLQueryを発行
import sys
import ibm_db
def main(dict):
 rows = []
 conn = ibm_db.connect("DATABASE=BLUDB;HOSTNAME=dashdb-txn-sbox-yp-dal09-04.services.dal.bluemix.net;PORT=50000;PROTOCOL=TCPIP;UID=xxx99999;PWD=xxxxxxxxxxxxxxxxxxx;","","")
 sql = 'SELECT "name", "content", "Link", "remark" FROM MPX09999.TABLE01 WHERE upper("name") like upper({})'.format(dict["name"])
 stmt = ibm_db.exec_immediate(conn, sql)
 row = ibm_db.fetch_tuple(stmt)

 while (row):
    rows.append(row)
    row = ibm_db.fetch_tuple(stmt)

 return { 'result': rows }
```

- DB名：MPX09999
- Table名：TABLE01
- Watson Assistantからの引数："name"  
Db2資格情報となる以下の太文字の箇所は、Db2画面のサービス資格情報のdsn設定からコピーする。  
conn = ibm_db.connect(**"DATABASE=BLUDB;HOSTNAME=dashdb-txn-sbox-yp-dal09-04.services.dal.bluemix.net;PORT=50000;PROTOCOL=TCPIP;UID=xxx99999;PWD=xxxxxxxxxxxxxxxxxxx;"**,"","")
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/90a02bac-f9f7-a404-79ec-d5d1026ace06.png)

# Watson Assistantの設定
Watson Assistantのインスタンスは「Watson AssistantとSlack連携」で作成済
1. Dialogを開く。 
2. Welcomeのようこその中に、Functionsの認証情報をセットする。（Dialogの中は下記画像のように作成していますが、あまり詳しくなくテスト用で作成しているため、適宜修正して使用してください）  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/dc0eacc8-f4c5-6d25-e283-73d51f59c5ce.png)

```json:Watson_assistantの[ようこそ]JSON

{
  "context": {
    "icf_cred": {
      "user": "3fbf5059-f5d0-4a17-baee-79cf0a2a20d5",
      "password": "ogp2LnjgCmCAeqjg8IV0CnGi6A1NbHY556ZU53p1x0OAn4UPGvZ8eX0bjfGH8H66"
    }
  },
  "output": {
    "generic": [
      {
        "values": [
          {
            "text": "検索キーワードを入力してください。"
          }
        ],
        "response_type": "text",
        "selection_policy": "multiline"
      }
    ]
  }
}
```

Functionsの認証情報として設定しているicf_credのuserとpasswordは、Functionsの名前空間の設定からコピーしてくる。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/e76f2967-a322-4dc7-2394-372a406ebb14.png)  
2. 子ノード（ここでは@Keyword or #Keyword_intent）のJSONを以下のように設定する。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/d3b6ee9d-5dff-d03f-974f-32e76498daf2.png)

```json:JSON
{
  "output": {
    "generic": []
  },
  "actions": [
    {
      "name": "/HogeHoge-039485_hogehogespace/hogehoge-application",
      "type": "cloud_function",
      "parameters": {
        "name": "'%@Keyword.literal%'"
      },
      "credentials": "$icf_cred",
      "result_variable": "context.my_result"
    },
    {
      "name": "/HogeHoge-039485_hogehogespace/hogehoge-application",
      "type": "cloud_function",
      "parameters": {
        "result1": "'<? $my_result.result[0][0] ?>'",
        "result2": "'<? $my_result.result[0][1] ?>'",
        "result3": "'<? $my_result.result[0][2] ?>'",
        "result4": "'<? $my_result.result[0][3] ?>'",
        "keyword_input": "'<? input.text ?>'",
        "keyword_intents": "'<? intents ?>'",
        "keyword_entities": "'<? @Keyword.literal ?> '"
      },
      "credentials": "$icf_cred",
      "result_variable": "context.log_result"
    }
  ]
}
```

- "name"：　[Cloud Foundry組織]_[Cloud Foundryスペース]/[Functions名]とする
- "type"：　cloud_functions固定
- "parameters"の"name"：　Functionsのname引数へ渡す値。@Keyword.literalはエンティティの値を渡す設定としている。%で囲んでいるのはSQLのQueryで曖昧検索を行うため。'で囲んでいるのは、FunctionsのSQL文で文字列として認識されるようにするため。要は、@Keywordエンティティの文字を曖昧検索するようにしている。
- "credentials"：　Welcomeで設定したFunctionsの認証情報
- "result1"：　FunctionsのSQLのSelect文で戻ってくる最初の列。ここではname。$my_result.result[0][0]は、"result_variable": "context.my_result"のmy_resultから持ってきている。以下、result2以降も同様。
- "result2"：　FunctionsのSQLのSelect文で戻ってくる二番目の列。ここではcontent
- "result3"：　FunctionsのSQLのSelect文で戻ってくる三番目の列。ここではLink
- "result4"：　FunctionsのSQLのSelect文で戻ってくる四番目の列。ここではremark
- "keyword_input"：　Watson Assistantに入力した値。<? input.text ?>。'は、FunctionsのSQL文で使用するため追記。
- "keyword_intents"：　検索にヒットしたインテント。<? intents ?>。'は、FunctionsのSQL文で使用するため追記。
- "keyword_entities"：　検索にヒットしたエンティティ。<? @Keyword.literal ?>。'は、FunctionsのSQL文で使用するため追記。  

3. Resを以下のように設定する。Functionsを介してDb2 on Cloudにデータを書き込む。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90447/59bed3c4-56f1-1edb-d489-2e92eff2bb3f.png)  

```json:Slackに返信するResノードの中身
{
  "output": {
    "generic": [
      {
        "values": [
          {
            "text": "<? $my_result.result[0][0] ?>"
          },
          {
            "text": "<? $my_result.result[0][1] ?>"
          },
          {
            "text": "<? $my_result.result[0][2] ?>"
          },
          {
            "text": "<? $my_result.result[0][3] ?>"
          }
        ],
        "response_type": "text",
        "selection_policy": "multiline"
      }
    ]
  }
}
```
- "text"：　<? $my_result.result[0][0] ?>　Slackへの戻り値の1番目
- "text"：　<? $my_result.result[0][1] ?>　Slackへの戻り値の2番目
- "text"：　<? $my_result.result[0][2] ?>　Slackへの戻り値の3番目
- "text"：　<? $my_result.result[0][3] ?>　Slackへの戻り値の4番目


```python:Functions：hogehoge-logの中身：MPX09999.LOGテーブルにデータ挿入
import sys
import ibm_db
def main(dict):
 conn = ibm_db.connect("DATABASE=BLUDB;HOSTNAME=dashdb-txn-sbox-yp-dal09-04.services.dal.bluemix.net;PORT=50000;PROTOCOL=TCPIP;UID=xxx99999;PWD=xxxxxxxxxxxxxxxxxxx;","","")
 sql = 'INSERT INTO MPX09999.LOG(keyword_input,keyword_intents,keyword_entities,result1,result2,result3,result4) VALUES ({0},{1},{2},{3},{4},{5},{6})'.format(dict["keyword_input"],dict["keyword_intents"],dict["keyword_entities"],dict["result1"],dict["result2"],dict["result3"],dict["result4"])
 stmt = ibm_db.exec_immediate(conn, sql)
 return {'message': "keyword_intents"}
```

##所感
- Watson Assistantは詳しくないのでDialogのロジックとか変なのですいません。回答する内容によって微妙にエラーが出ます。
- たぶんこんな感じでレスが戻ってくるのですが、プログラムの組み方が悪いのか、それともFunctionsのレスポンスが悪いのか、ある程度の時間をおいてSlackから文字列を入力すると、Db2のデータのレスポンスが無くなります。ただ、すぐに2回目を入力するときちんとDb2データのレスポンスがありました。プログラミングが得意ではないので、Functionsの中身を工夫すればいい感じになるのかなー。。