Teams に ProActive メッセージ（Push 通知）を送る

# はじめに
今回、Teams へ Azure Bot Service を使用して ProActive メッセージを送る手順をまとめてみました。
一応、公式の GitHub の sample にもあります。
ちょっと違うのが、``Http Request を post`` にしていることです。 

今回は（も）Node.js を使用していきます。
また、コードは Github にあげるほどはないので、記事内にペタペタしときます。

また、Azure Bot Service の sample の試し方やDeploy方法は過去の以下の記事を参考にしていただけると嬉しいです！
- [local test 方法まで](https://qiita.com/MinoMinoMinoru/items/d02bcea1a7b681c40240)
- [Azure へのDeployなど](https://qiita.com/MinoMinoMinoru/items/410ea775baa9943f59ce)

# ProActive メッセージを  post で送信する際に必要な情報
基本的には body にconversation reference という情報が必要になります。
conversation reference には多くの情報があるのですが、以下の4つを最低限含めてあげたら大丈夫です。なので、ここから内容では基本的に以下の4つのみに注目して書きます。

```body.json
{
	"ref":{
		  "conversation": {
		    "id": ""
		  },
		  "bot": {
		    "id": "",
		    "name": ""
          },
		  "serviceUrl": ""
	}
}
```
基本的に、この形式を入れてあげたら、他にどんな情報でも自由に足してあげたら大丈夫です！
この辺の情報は chat ごとに作られます。ので、Teamsのこれらの情報がは特に大事にしてください。

# もとにした
今回は硬式sample の EchoBot の一部を編集します。
[こちらから手元に落としてください](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/02.echo-bot)

編集したのは
- index.js
- bot.js
の2つです。

まずは、サーバを起動する index.js の方を編集します。
下の折り畳みにコードが書いてるので、コピペでおｋです。

<details><Summary>index.js の全体 </Summary>
<div>

```index.js
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

const dotenv = require('dotenv');
const path = require('path');
const restify = require('restify');

// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter } = require('botbuilder');

// This bot's main dialog.
const { EchoBot } = require('./bot');

// Import required bot configuration.
const ENV_FILE = path.join(__dirname, '.env');
dotenv.config({ path: ENV_FILE });

// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
    // Important
    server.use(restify.plugins.bodyParser({ mapParams: true }));
    console.log(`\n${ server.name } listening to ${ server.url }`);
    console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
    console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});

// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about how bots work.
const adapter = new BotFrameworkAdapter({
    appId: process.env.MicrosoftAppId,
    appPassword: process.env.MicrosoftAppPassword
});

// Catch-all for errors.
const onTurnErrorHandler = async (context, error) => {
    // This check writes out errors to console log .vs. app insights.
    // NOTE: In production environment, you should consider logging this to Azure
    //       application insights.
    console.error(`\n [onTurnError] unhandled error: ${ error }`);

    // Send a trace activity, which will be displayed in Bot Framework Emulator
    await context.sendTraceActivity(
        'OnTurnError Trace',
        `${ error }`,
        'https://www.botframework.com/schemas/error',
        'TurnError'
    );

    // Send a message to the user
    await context.sendActivity('The bot encountered an error or bug.');
    await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};

// Set the onTurnError for the singleton BotFrameworkAdapter.
adapter.onTurnError = onTurnErrorHandler;

// Create the main dialog.
const myBot = new EchoBot();

// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
    adapter.processActivity(req, res, async (context) => {
        // Route to main dialog.
        await myBot.run(context);
    });
});

// Listen for Upgrade requests for Streaming.
server.on('upgrade', (req, socket, head) => {
    // Create an adapter scoped to this WebSocket connection to allow storing session data.
    const streamingAdapter = new BotFrameworkAdapter({
        appId: process.env.MicrosoftAppId,
        appPassword: process.env.MicrosoftAppPassword
    });
    // Set onTurnError for the BotFrameworkAdapter created for each connection.
    streamingAdapter.onTurnError = onTurnErrorHandler;

    streamingAdapter.useWebSocket(req, socket, head, async (context) => {
        // After connecting via WebSocket, run this logic for every request sent over
        // the WebSocket connection.
        await myBot.run(context);
    });
});

// add to echo sample
// Listen for incoming notifications and send proactive messages to users.
server.post('/api/notify',async (req, res) => {
    await MicrosoftAppCredentials.trustServiceUrl(req.body.ref.serviceUrl);
    await adapter.continueConversation(req.body.ref, async turnContext => {
        // console.dir(req.body.ref.serviceUrl);
        await turnContext.sendActivity('ProActive Hello!');
    });
    
    res.setHeader('Content-Type', 'text/html');
    res.writeHead(200);
    res.write('<html><body><h1>Proactive messages have been sent.</h1></body></html>');
    // res = JSON.stringify(req);
    res.end();
});

```
</div>
</details>

注意すべきは server.listen の部分で restify のプラグインを追加しているところ、server.post のエンドポイントを実装しているところです。 また、Loacalでテストする際には```await MicrosoftAppCredentials.trustServiceUrl(req.body.ref.serviceUrl);``` の部分をコメントアウトしてないとエラーが出るかもです（確かここはTeams上で実行するのに必要な部分）

```index.js
---- 抜粋 ----
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
    // Important
    server.use(restify.plugins.bodyParser({ mapParams: true }));
    console.log(`\n${ server.name } listening to ${ server.url }`);
    console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
    console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});

---- post 処理部分 ----
// Listen for incoming notifications and send proactive messages to users.
server.post('/api/notify',async (req, res) => {
    await MicrosoftAppCredentials.trustServiceUrl(req.body.ref.serviceUrl);
    await adapter.continueConversation(req.body.ref, async turnContext => {
        // console.dir(req.body.ref.serviceUrl);
        await turnContext.sendActivity('ProActive Hello!');
    });
    
    res.setHeader('Content-Type', 'text/html');
    res.writeHead(200);
    res.write('<html><body><h1>Proactive messages have been sent.</h1></body></html>');
    // res = JSON.stringify(req);
    res.end();
});

```

次に、Bot.js を編集します。
こちらは、Bot との会話のやり取りをするときに使用しています。
今回は、ProActive メッセージを送ることのフォーカスしているので、
あんまり実装はしてませんが、ConverstaionReference を取得するのに少し編集してます。

<details><Summary>bot.js の全体 </Summary>
<div>

```bot.js
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { ActivityHandler, MessageFactory } = require('botbuilder');

// add to echo sample
const { TurnContext } = require('botbuilder');

class EchoBot extends ActivityHandler {
    constructor() {
        super();
        // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
        this.onMessage(async (context, next) => {

            // add echo sample from
            const cvid = this.getConversationReference(context.activity);
            console.dir(cvid)
            var msg = `Your Info`;
            await context.sendActivity(msg);
            msg="- conversation reference:"+cvid.conversation.id+"\n\n";
            msg+="- Bot id:"+cvid.bot.id+"\n\n";
            msg+="- Bot name:"+cvid.bot.name+"\n\n";
            msg+="- Service URL:"+cvid.serviceUrl+"\n\n";
            console.log(msg);
            await context.sendActivity(msg);
            // to here

            await next();
        });

        this.onMembersAdded(async (context, next) => {
            const membersAdded = context.activity.membersAdded;
            const welcomeText = 'Hello and welcome!';
            for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
                if (membersAdded[cnt].id !== context.activity.recipient.id) {
                    await context.sendActivity(MessageFactory.text(welcomeText, welcomeText));
                }
            }
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });
    }

    // add echo sample from
    getConversationReference(activity) {
        // ConversationReference の取得
        const conversationReference = TurnContext.getConversationReference(activity);
        return conversationReference;
    }
    // to here
}

module.exports.EchoBot = EchoBot;

```
</div>
</details>

大した長さがないですが、

```bot.js
// add to echo sample
const { TurnContext } = require('botbuilder');
```
というのを忘れがちなので一応抜粋しておきます。
（もしかしたら、このmoduleを足さなくても別の方法でも実装できるかもです。）
（もしあれば教えてください(笑)）

とりあえず、一度 Local でテストしてみましょう。
やり方は最初にも言いましたが、[これを参考にしてください。](https://qiita.com/MinoMinoMinoru/items/d02bcea1a7b681c40240)
Emulator で実行して何かしら話しかけたらこんな感じで返してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248829/93e238dd-451f-e04c-1ea9-d3ae0d647f13.png)

ちなみに、Emulator だと ServiceURL や id がEmulatorを更新するたびに変更されるので注意してくださいね。そして、上記の body.json にBotから変えてきた情報を入れて post してあげると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248829/b2f0b42d-925f-db8a-837c-4af45fac9bd2.png)

こうなります！ちゃんと PoroActive にメッセージが送られてきてますね！

後は、AzureにBotをDeployして、Teamsとの接続を確認しましょう。
[例によって、Azure へのDeployなどはこちらから確認してください(笑)](https://qiita.com/MinoMinoMinoru/items/410ea775baa9943f59ce)

さて、Deploy が済んだら、BotServiceのリソースに移動して、チャンネルから、Teamsを選んでください。（私のはすでにchannelが出来ているので、少し表示されている場所は違うかと思いますが）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248829/4882397c-9238-d555-c5b0-610d4b9b213e.png)

ここをクリックして、画面の指示通りに何回かクリックしたら、Teams を Web かデスクトップアプリで開くように指示されますので、開いてください。そしたら、先ほどまで作成していた Bot とのChat が出来ているはずです。


ここまで来たらもう少しです。Teams に Proactive メッセージを送る際のpost Request の URL は ``App Service のEndpoint``を指定してあげてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248829/f00ce252-e4b3-7da4-0e07-5736d1776c40.png)

この画像の右上にある URL ですね。
以上です！いろいろとつたない文章でしたが、ご興味があります方はぜひお試しくださいな！