RPAとの会話をMicrosoft Teamsに集約するしくみを作成してみた (Automation Anywhere v11編)

RPAでタスクを実行するときに、ロボットが判断できないことを人間に助言を求めたり、情報提供したり、といったことが求められるケースがあります。また、不測の事態でエラーが起こった時に人間に救助を求めることもあるでしょう。

Automation Anywhereにおいて、人間とのやり取りが発生するのは以下のケースになります。

* Message Box
* Prompt For Value
* Prompt For File
* Prompt For Folder
* Prompt For Yes/No
* Error 時のエラーメッセージ

Automation Anywhereでは、情報を人間に提供する手段として電子メール (**Send Email**)や、エラー時の通知メール (**Error Handling**)を標準サポートしていますが、これを、よりインタラクティブなチャットツールであるMicrosoft Teamsに出力するしくみを作ってみたいと思います。また、ユーザーからの入力要求 (**Prompt for XXX**) はメールだけではなかなか実装が難しいのですが、Microsoft Teamsを使うとこれも実現することが可能です。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c52c1a3b-fdb5-2e33-4049-17e93ddc73e2.png)


環境

* Automation Anywhere Enterprise 11.3.3
* Microsoft Teams (2020/1/28版)
* Azure Functions ランタイムバージョン v3.0.13107 (~3.x)
* インターネットにつながりHTTP通信が、使用されるURLに対して通ること


**参考情報**

* [Automation Anywhere でユーザープロンプトを出すには - Qiita](https://qiita.com/RPAbot/items/118b6ce81bf698379bda)
* [RPAで作ったBotからMicrosoft Teamsに投稿してみる（Automation Anywhere編）- Qiita](https://qiita.com/zamaezaaa/items/9b2994bc671a2375f2fd) by @zamaezaaa さん
* [Microsoft Teams に PowerShell で投稿してみる - Qiita](https://qiita.com/miyamiya/items/ccae505b8e97b5b532a7) by @miyamiya さん
* [コネクタと Webhook にメッセージを送信する - Office デベロッパーセンター](https://docs.microsoft.com/ja-jp/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using)
* [操作可能なメッセージ カードを Office 365 グループに投稿する - Office デベロッパーセンター](https://docs.microsoft.com/ja-jp/outlook/actionable-messages/send-via-connectors)

# 全体像
通常はロボットが動いているPCの画面操作を通して、人間とロボットの会話が行われますが、これをリモートのMicrosoft Teamsクライアント (PC、ウェブ、モバイル等どれでもよい) を通して行おうというのが趣旨になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/1a260b10-2d3d-b4b5-00d6-44f0596b75e0.png)

技術的には、ロボットからMicrosoft Teamsへの情報のポストは、Teams側のREST APIであるWebhookを通して比較的簡単に実装ができますが、Teamsからロボットへの情報のポストは、専用のウェブサービスを間に立てる必要があります。Teamsでアクションが行われた結果を専用のウェブサービスを呼び出してポストし、ロボットはそのWebサービスを定期的にリッスンして状況が変わったらそれに反応するという手法をとる必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/8b311d14-7161-822e-8923-8ac3d4a78485.png)

仲介ウェブサービスのURLの例: ("tMXShyxV8HkosUE" はセッション文字列)

|URL|備考|
|:--|:--|
|`https://<アプリ名>.azurewebsites.net/api/OK-GET/?key=tMXShyxV8HkosUE`|このセッション文字列に対してウェブサービスが呼ばれていれば、HTTP Status 200 OKを返す。|
|`https://<アプリ名>.azurewebsites.net/api/OK-SET/?key=tMXShyxV8HkosUE`|呼ばれると、以後、セッション文字列に対応して返す値がHTTP Status 200 OKに変わる。|


また、普段RPAがローカルPCの画面に出しているメッセージは**モーダル**であることに注意してください。Microsoft Teamsへのメッセージはモードレスなので、同じ動作をさせるにはメッセージとアクションボタンのポストを行った後、たとえば10秒ごとにウェブサービスをリッスンしてその間ループするようなロジックを組む必要があります。

> 注: この記事では、専用のウェブサービスの実装の詳細については触れません。このWebサービスのインターフェイスの仕様 (入出力) についてのみ触れます。ウェブサービスの作成については別の記事『[Azure FunctionsでMS TeamsからRPAへの会話を仲介するウェブサービスを作成してみた](https://qiita.com/RPAbot/items/3c0f5937c8e40bf7f99b)』をご覧ください。

# Microsoft Teams のチャンネルでWebhookを設定、取得
まず最初にMicrosoft Teams側の設定を行い、チャンネルにロボットがメッセージを送信できるようにします。
Teamsで外部サービスからメッセージを送信できるのは、チャンネルと呼ばれるOffice 365 Groupsの一部の機能で、ユーザー同士のグループチャットや、任意のユーザーへのチャットでメッセージを送信することは今のところできないようです。

そのため、まずはRPAからメッセージを受け取るためのチャンネルを決めます。ない場合は新しく作成してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/67c5a0dc-c3de-324d-2575-1fb96d882bea.png)
チャンネル一覧の中のチャンネル名の右側に出る「その他のオプション」メニュー (「...」)か、画面右上の「その他のオプション」メニュー (「...」)をクリックして、**「コネクタ」メニュー**を見つけてクリックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/82ac925c-9d63-b3da-7af3-ed465cc487ac.png)
コネクタ一覧では、「**Incoming Webhook**」を探して追加してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c1087c21-e622-def1-e2d3-19f7851875ea.png)
Incoming Webhookとは、マイクロソフトが提供する純正コネクタで、外部のサービスからOffice 365グループにリアルタイムでデータを送るコネクタです。「追加」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/7ad479db-f48b-ebf6-f1e4-809216ec7d58.png)
次の画面では、Webhookの「名前」と「イメージ」を指定します。この名前がMicrosoft Teams内でのチャットボットの名前、イメージ画像がチャットボットのプロファイル画像となります。名前にはひとまず「AAロボット」と入力してみましょう。次に「作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/b5907691-3c7b-4c54-1cb0-e4bbc0be356a.png)
すると、コネクタで設定した内容が改めて表示されます。この中で、

```
https://outlook.office365.com/webhook/a1269812-6d10-44b1-abc5-b84f93580ba0@9e7b80c7-d1eb-4b52-8582-76f921e416d9/IncomingWebhook/3fdd6767bae44ac58e5995547d66a4e4/f332c8d9-3397-4ac5-957b-b8e3fc465a8c
```
のような形式で表示されている、このWebhookのURL (URI) をコピーしておいてください。これはAutomation Anywhere Enterprise (AAE)  Client内で使います。最後に「完了」ボタンをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/929eebcd-7d68-38ce-98ec-b11c6e9c9ea7.png)
すると、Teamsのチャンネルに以下のような投稿がなされ、正常に設定が完了したことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/db775e2a-ea33-13ca-da33-882a5a964552.png)


# TaskBotのREST Web ServicesでMicrosoft Teamsへの投稿の動作確認する
次にAAE Clientで新しいワークベンチを開きましょう。コマンドリストの中から「**REST Web Service**」をドラッグ＆ドロップします。ダイアログボックスが起動するので、URIに先ほどのWebhookのURLを指定し、メソッドを「GET」から「POST」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/6dd4885d-7e54-28eb-08b9-a3338b393841.png)
次に縦ナビゲーションの「ヘッダー」をクリックして、`Content-Type`を`application/json`に変更します。つまり、REST Web ServiceのデータにはJSON形式のデータを使うことを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/53216cd9-cc9a-1cef-ffef-c15c58063a7b.png)
次に横ナビゲーションの「PARAMETER」をクリックして、縦ナビゲーションの「Post」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/37799855-6fcd-6fda-e2bf-7b5b21ab78eb.png)
テキストボックス内に、JSON形式のデータをコピー＆ペーストします。"text"属性は、投稿するメッセージの内容です。たとえば以下のように指定できます。

```JSON:PARAMETER/POST
{
"text" : "こんにちは。"
}
```
このテキストの中には変数も指定できます。実際に指定する文字列は`$Message$`変数内に格納して指定することにするため、ここでは変数を入れた以下の形式の文字列をコピー＆ペーストしてください。

```JSON:PARAMETER/POST
{
"text" : "$Message$"
}
```

>注: 変数を挿入する場合は、変数の周りをダブルクオートで囲うことも忘れないでください。囲わない場合はWebサービスがエラーになります。(HTTP400: BadRequest)

最後に、このWebサービスを実行した結果の戻り値を格納する変数を指定します。横ナビゲーションの「レスポンス」をクリックして、「レスポンスの保存先」に`Prompt-Assignment`を指定しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/1659f073-b617-4498-f39a-332dae7d0db1.png)
その後「保存」ボタンをクリックすると、REST Web Serviceの設定は完了です。

> `Prompt-Assignment`は正常終了 (HTTP Status=200 OK) の場合は"1"、そうでない場合はエラーメッセージ文字列が格納されます。

### メッセージの投稿
アクションリストにはREST Web Serviceコマンドが追加されたことが確認できます。先ほどの`$Message$`変数を、変数マネージャーから追加します。ひとまず、以下のようなテストメッセージを入れておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/3a81d5ee-f8da-a1ec-8dc9-43b96e03992b.png)
`$Message$`変数を保存後、Botの実行をすると、以下のメッセージがMicrosoft Teamsのチャンネルに投稿されます。これでもう任意のメッセージをTeamsに投稿できてしまいますね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/bdd6e6d1-396c-4639-7fab-ef6c345a8fc2.png)

ちなみに、このtext属性ですが、簡単なmarkdownの書式をサポートしています。(リファレンスは『[従来の操作可能なメッセージ カード リファレンス](https://docs.microsoft.com/ja-jp/outlook/actionable-messages/message-card-reference)』)に記載されています。

`$Message$`変数に以下のようなテキストを入れることもできます。

```HTML:\$Message\$変数
# テスト
[Microsoft](https://www.microsoft.com)からの**メッセージ**

* 項目1
* 項目2
```

投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/becce73a-deaf-0d21-faf8-188352fe3ae3.png)

また、text属性以外にもtitle属性、themeColor属性なども利用して様々な装飾が可能です。

```JSON:PARAMETER/POST
{
"title": "ロボットからのメッセージ",
"themeColor": "0072C6",
"text" : "$Message$"
}
```

```HTML:\$Message\$変数
処理が完了しましたのでお知らせします。
```

投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/6011a5f1-6bc2-9ad2-bb0e-739875e960ba.png)

もっと複雑な表現も可能です。

```JSON:PARAMETER/POST
{
    "themeColor": "0076D7",
    "summary": "新しいお知らせ",
    "sections": [{
        "activityTitle": "![TestImage](https://47a92947.ngrok.io/Content/Images/default.png)$Message$",
        "activitySubtitle": "サンプルプロジェクト",
        "activityImage": "https://teamsnodesample.azurewebsites.net/static/img/image5.png",
        "facts": [{
            "name": "担当者:",
            "value": "未定"
        }, {
            "name": "期限",
            "value": "2019/12/24"
        }],
        "markdown": true
    }]
}
```
投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c37b5e64-64b2-948d-c275-ab5846b34e42.png)


さて、ここまでであればWebhookの実装のみで可能なのですが、通常のMessage Boxのようにモーダルなダイアログボックスのような動きをさせる、つまり表示したメッセージに対して人間が「OK」ボタンを押すまでは先の処理に進まないようにするには、人間からのフィードバックをRPAに伝えるための専用のWebサービスの実装とそれを使うための実装が必要です。投稿メッセージには「OK」ボタンを表示するようにします。また、この「OK」ボタンを押したときには、専用のWebサービスにポストする動作を実装します。

REST Web Serviceコマンドには以下をコピー＆ペーストします。Value型変数`$Rand$`を追加しておき、15文字のRandom文字列を生成するRandom型文字列生成変数 `$GenRand$` から値を代入しておきます。

```JSON:PARAMETER/POST
{
"title": "ロボットからのメッセージ",
"themeColor": "0072C6",
"text" : "$Message$",
"potentialAction": [
   {
          "@type": "HttpPOST",
          "name": "OK",
          "isPrimary": true,
          "target": "https://<アプリ名>.azurewebsites.net/api/ok-set/?key=$Rand$"
   }
 ]
}
```
投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/39b57eb1-4b7d-b523-d564-7d1de1419c38.png)
「OK」ボタンを押すと、専用のWebサービスが呼ばれます。

```:URLの例
https://<アプリ名>.azurewebsites.net/api/ok-set/?key=tMXShyxV8HkosUE
```
TaskBotの方は以下のようにループを実装しておきます。(Webhookのパラメータは`$WebHookParam$`変数の初期値として格納)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/62f1c0c4-20ae-6587-9e9b-824659f140cd.png)

5行目: ループは`$Prompt-Assignment$`が空白の間継続するものとします。
8行目: OKボタンが押されたかどうかを監視するウェブサービス (OK-GET)は、押されていない間は空白を、ボタンが押されるとボタンの種類を表す数字 (OK=1)を返すものとします。
10行目: OKボタンが押されてループを抜けたら、Teamsに再度メッセージを送信 ("OKボタンが押されたことを確認しました。"など)して、Teamsを使っている人間に認識させます。

### ユーザーにオプションを選択させる場合
**Prompt for Yes/No** に当たるものは、以下のJSONでTeamsのUIの設定が可能です。

```JSON:PARAMETER/POST
{
"title": "ロボットからのメッセージ",
"themeColor": "0072C6",
"text" : "$Message$",
"potentialAction": [
   {
      "@type": "ActionCard",
      "name": "Send Feedback",
      "inputs": [
        {
          "@type": "MultichoiceInput",
          "id": "list",
          "title": "選択してください",
          "style": "expanded",
          "choices": [
            { "display": "はい", "value": "1" },
            { "display": "いいえ", "value": "2" }
          ]
        }
      ],
      "actions": [
        {
          "@type": "HttpPOST",
          "name": "OK",
          "isPrimary": true,
          "target": "https://<アプリ名>.azurewebsites.net/api/yesno-SET/?key=$Rand$",
          "body": "list={{list.value}}"
        }
      ]
   }
 ]
}
```
投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/9af8a669-7b02-8803-b195-6b3e4c7caae3.png)

>注: `"name": "Send Feedback"`は、Teamsクライアントのプラットフォームによっては (例: iPhone)、以下のようにフィードバックを送るセクションがあらかじめ折りたたまれており、ボタンを押すことにより広がる仕組みになっていて、そのボタン名に使われます。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/7364d838-5810-fffa-a45c-c3f2f58d47d6.png)


### ユーザーからのテキスト入力を受け付ける場合
**Prompt for Value** に当たるものは、以下のJSONでTeamsのUIの設定が可能です。

```JSON:PARAMETER/POST
{
"title": "ロボットからのメッセージ",
"themeColor": "0072C6",
"text" : "$Message$",
"potentialAction": [
   {
      "@type": "ActionCard",
      "name": "Send Feedback",
      "inputs": [
        {
          "@type": "TextInput",
          "id": "comment",
          "isMultiline": true,
          "title": "ここにテキストを入力してOKを押してください"
        }
      ],
      "actions": [
        {
          "@type": "HttpPOST",
          "name": "OK",
          "isPrimary": true,
          "target": "https://<アプリ名>.azurewebsites.net/api/comment-set/?key=$Rand$",
          "body": "comment={{comment.value}}"
        }
      ]
   }
 ]
}
```
投稿されるメッセージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/9995e194-f5bb-38ab-a1ea-bce09269de83.png)

# 最後に: Metabot化による部品化、リッスン⇒トリガー実装の工夫によるライセンス効率化

これらの処理をMetaBotしておくと、Microsoft Teamsとの連携を簡単に行うことができますよ！

>また、ウェブサービスのリッスンをRPAから直接行ってしまうと、リッスンしている間はRPAの実行ライセンスをひとつ消費してしまいます。リッスンしている時間が長くなると、ライセンスに対する投資対効果が低くなってしまいます。そのような場合はリッスンをする仕組みは別に作って、条件が満たされたらRPAをトリガーで起動するなどするつくりにすると、RPAライセンスをより効率的に利用できます。

それではまた！