Slack => MicrosoftFlow => MicrosofTeams

最近Microsoftから新サービスとして、[MicrosoftFlow](https://flow.microsoft.com/ja-jp/)と[MicrosoftTeams](https://products.office.com/ja-jp/microsoft-teams/group-chat-software)が法人向けのOffice365ユーザーを対象にプレビューとして公開されました。

どちらも面白そうだったのですが、なぜかFlowとTeamsが連携する口が存在していなかったので、
試しに無理やり連携してみました。結構ハマったのでログとして残しておきます。
## 概要

SlackのOutboundsMessageを利用してMicrosoftFlowへ通知を行い、MicrosoftFlowからMicrosoftTeamsのInboudsMessageへの通知を行います。イメージ的にはこんな感じになります。

TODO あとで絵を貼る

## 動機
ウチの会社はセキュリティ上の問題でSlackを業務で利用することができないという~~微妙な会社~~セキュリティが非常に堅牢な会社となっておりますが、MS製品はなぜか許容されるケースが多いのでMicrosoftTeamsの導入を狙ってたりします。

## 書かないこと

* MicrosoftTeamsのサインアップ方法&有効化  
  (ただ、 **URLがわからなくてすごく苦労したことは主張しておきたい!!** => [https://teams.microsoft.com/](https://teams.microsoft.com/)が実際のURLです)
* MicrosoftFlowのサインアップ方法&初期設定
* Slackのサインアップ方法&初期設定


## 手順

以下の手順で構築します。1-5までは簡単です。

1. MicrosoftFlowでフロー(トリガー)を設定
2. SlackのOutBoudsMessageを設定
3. MicrosoftTeamsのInBoundMessageを設定
4. MicrosoftFlowでフロー(アクション)を設定
5. 疎通確認
6. MicrosoftFlowでデータの加工

### 1. MicrosoftFlowsでフロー(トリガー)を設定

1. [Microsoft Flow](https://flow.microsoft.com/ja-jp/)にサインイン
2. ヘッダーにある**自分のフロー**を選択
3. **一から作成**をクリック(なんで『一』という漢字なんだろう...)
4. 適当なフロー名をつける
5. 検索ボックスに**要求**と入力して合致したトリガーを追加
6. **+新しいステップ** => **アクションの追加**から**応答**と入力しアクションを追加。
7. 一度保存しないとURLが生成されないので一旦**フローの作成**で保存する。
8. **要求**で新規URLが構築されるのでコピーしておく

<img width="510" alt="要求〜応答.png" src="https://qiita-image-store.s3.amazonaws.com/0/11214/c7177a3e-4ded-3fc0-ba67-66409164be8e.png">

※5.のタイミングで保存しようとしても`トリガーとアクションが少なくとも 1 つずつ必要です`と警告が出て保存できない。

### 2. SlackのOutBoudsMessageを設定
SlackのIntegration機能である[Outgoing Webhooks](https://api.slack.com/outgoing-webhooks)を利用して
Slack上への投稿をトリガーにMicrosoftFlowに対して通知を行います。

1.[Outgoing WebHooks](https://slack.com/apps/A0F7VRG6Q-outgoing-webhooks)の**Add Configuration**から、**Add Outgoing WebHooks integration**を選択する。
2.**Integration Settings**を以下のように設定する

| key | value  |
|---|---|
| Channel | 監視したい任意のchannel |
| Trigger Word(s) | 全て処理したいので入力しない |
| URL(s) | 1-8.でコピーしたURLを設定する |
3.**Save Settings**を押して保存
4.2.のChannelで設定したtimelineに***added an integration to this channel: outgoing-webhook***と表示されることを確認。

### 3. MicrosoftTeamsのInBoundMessageを設定
1. [MicrosoftTeams](https://teams.microsoft.com/)にログインして任意のチームを開き、**一般の横にある...**をクリックし、**コネクタ**を選択
2. **Incoming WebHook**の**追加**をクリック
3. 任意の名前を入力して**作成**をクリック
4. **https://outlook.office365.com/webhook/XXXXXXXXXX...**的なURLが生成されるのでコピーする

### 4. MicrosoftFlowsでフロー(アクション)を設定
1.再度[Microsoft Flow](https://flow.microsoft.com/ja-jp/)へ。
2.**自分のフロー**から先ほど作成したフローを選択
3.**+新しいステップ**から**アクションの追加**を選択
4.HTTPを選び以下の様に設定する

| key | value  |
|---|---|
| 方法 | POST |
| URI | 3-4でコピーしたURL |
| ヘッダー | 空(何も設定しない) |
| 本文 | { "text": "hello" } |
5.D&Dで作成したHTTPを移動して、要求 => HTTP => 応答という形に移動する

<img width="491" alt="要求-http-応答.png" src="https://qiita-image-store.s3.amazonaws.com/0/11214/d3277d0a-46b7-9a52-428d-2c2847545bba.png">

### 5. 疎通確認
これで接続まではできるはずなので、疎通確認を行う。
2-2.で設定したchannelで任意のコメントを入力した場合に、実行の一覧表示にログが表示され、3-1で選択した、フロー上にHelloと表示されていればOK

### 6. MicrosoftFlowで詳細設定を行います。
さて、ここからが本題です。

Slackのoutgoing-webhookはJSON形式になっておりbase64でエンコードされたパラメーターを渡してくれるのですが、
これをMicrosoftFlow上で加工した上でMicrosoftTeamsに投げる必要があります。

#### 6-1. POSTの内容をJSONスキーマでマッピングする
まずは、**要求**を編集して**要求本文のJSONスキーマ**を設定します。  

ここのフォーマットは[JSON Schema](http://json-schema.org/)形式で定義することでプロパティをActionの引数として利用可能になります。Slackのoutgoing-webhookを受け取りたい場合はこんな形で定義します。

```json
{
  "properties": {
    "$content": {
      "type": "string"
    }
  },
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "$content"
  ]
}
```
こうすることで、動的なコンテンツとして$contentが利用可能になります。  
<img width="928" alt="content-avilable.png" src="https://qiita-image-store.s3.amazonaws.com/0/11214/0b835326-a961-a835-20dd-31f7f9e884c8.png">

#### 6-2. マッピングされたデータの解析と加工
次に**+新しいステップ**から**作成**を選択し$contentをHTTPで送信する形に加工します。

加工する際は[WorkflowDefinitionLanguage](https://msdn.microsoft.com/library/azure/mt643789.aspx)として簡易的なfunctionがいくつか提供されていますので、これらの関数を利用することが可能です。  
(かなり限定的なfunctionしかありませんが...)。

今回は`text=xxxx`という値を取得するために、以下のように**入力**を設定しました。

```
"@{decodeUriComponent(json(concat('{"', replace(replace(decodeBase64(triggerBody()['$content']), '=', '":"'), '&', '","'), '"}')).text)}"
```   

かなり無茶してるのでざっと分解しておきます。

```bash
@{
  # 5. encodeURIComponentされているので、戻します。
  decodeUriComponent(
    # 4. json化した上でドットオペレーターを通してtext文字列を取得します。
    json(
      # 3. key=value&key2=valueの形式を無理やりjsonにするためにreplaceをかけています。
      #    `=`を`:`に、`&`を`=`にした上で、`{}`を付与してJSONでparseできる文字列に変換します。
      concat(
        '{"',
        replace(
          replace(
            # 2. $contentがbase64でencodeされているのでdecodeします
            decodeBase64(
              # 1. 要求$contentを指します。動的コンテンツから追加した場合にfunctionを組み合わせるとエラーになるので直接書いています。
              triggerBody()['$content']
            ),
            '=',
            '":"'
          ),
          '&',
          '","'
        ),
        '"}'
      )
    ).text
  )
}
```

#### 6-3. 加工されたデータをHTTPで送信する
HTTPの本文を以下のように設定します。
保存するとわかりますが、`{ "text": "@outputs('作成')" }`に実際は置換されます。
<img width="460" alt="スクリーンショット 2016-11-08 12.51.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/11214/4c561341-e3ab-6df0-2f5d-8c5630ae72a5.png">

これでtext本文をMicrosoftTeamに送信可能になります。
今回はテキストだけ送っていますが、[いろいろサポート](https://dev.outlook.com/connectors/getstarted)していて結構リッチなメッセージも送れます。

結果的にはこんな感じのワークフローになりました。

<img width="494" alt="スクリーンショット 2016-11-08 13.17.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/11214/f529d58e-7034-d471-94ab-95f4b0dfc89c.png">


## オチ
まぁ、そのうち公式にFlowがSlackを受け取れるようになるか、
TeamsがSlackの通知を受け取れるようになるでしょう(というかしてください)。

## はまったところ

### 保存したFlowが消える
上記のように長めの関数を書いたところ、フローを保存した後に**"**だけしか残らないという悲しい目にあいました。コマンド自体は正しく動いてはいるのですが。。。**作成で入力**をする前に一旦ローカルにバックアップを取る事を強くお勧めします。

### リファレンスどこ
参考サイトを参考にしてください。[Workflow Definition Language](https://msdn.microsoft.com/library/azure/mt643789.aspx)が一番参考になります(大枠を理解していれば)

あとで書く
＊ 作成したHTTP本文が消せなくなる
＊ デバッグ方法


## 参考
* [Workflow Definition Language](https://msdn.microsoft.com/library/azure/mt643789.aspx)
* * [Calling Microsoft Flow from your application](https://flow.microsoft.com/en-us/blog/call-flow-restapi/)