ほりひログ

所属組織の製品 (Azure とか) に関連する内容が多めだけど、個人の見解であって、所属組織を代表する公式情報ではないです。

Bot Framework v4 を Azure Functions で使ってみる

仕事の Teams にボットでも作ろうかと思い、初めて Bot Framework SDK を使ってみました。

実は Teams のボットは Outgoing Webhook でも作れます。
しかし、Outgoing Webhook に対するボット アプリからのメッセージは、その Webhook リクエストに対するレスポンスで返す必要があること、そしてそのレスポンスは 5 秒以内に返す必要がある、など制限があります。
これをAzure Functions の HTTP トリガー関数で実装すると、関数処理自体が 5 秒以内に限定されてしまいますし、利用プランによってはコールド スタートするのでもっと処理時間は短くなり (最悪、処理を開始する前にタイムアウトする)、あまり相性がいいとは言えません。

なので、今回は Bot Framework SDK を使ったボット作成にチャレンジしました。

docs.microsoft.com

今のバージョンは v4 らしいです。

とりあえず Web 検索すると、下記の Qiita の記事がヒットします。

qiita.com

「Function Bot」というリソースからテンプレートがダウンロードできそうなので Azure デプロイしてみます。





。。。



もうないみたいです orz f:id:horihiro:20201031145013p:plain

上の記事が書かれた当時は Bot Framework が v3 のようですが、FUnction Bot は v3 のみに対応してて、そして v3 は 2019 年 8 月にディスコンになったようです😥

blog.botframework.com

ないものはしょうがないので、試しに Web App Bot をデプロイして、Web App Bot のテンプレートをダウンロードしてみます。

所詮 HTTP サーバー。そんな大変なことはないはず。

メインの index.js を開いてみると、express ベースみたいです。

const express = require('express');

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

// Import bot definitions
const { BotActivityHandler } = require('./botActivityHandler');

// (略)

// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about adapters.
const adapter = new BotFrameworkAdapter({
    appId: process.env.BotId,
    appPassword: process.env.BotPassword
});

// (略)

// Create HTTP server.
const server = express();
const port = process.env.port || process.env.PORT || 3978;
server.listen(port, () => 
    console.log(`\Bot/ME service listening at http://localhost:${port}`)
);

// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
    adapter.processActivity(req, res, async (context) => {
        // Process bot activity
        await botActivityHandler.run(context);
    });
});

最後の方の BotFrameworkAdapter#adapter にリクエストとレスポンスをセットで渡せればいい感じです。

何も考えず、HTTP トリガーに置き換えてみます。typescript です。

// (いろいろ省略)

const httpTrigger: AzureFunction = async function (context:Context) {
  await adapter.processActivity(context.req, context.res, async (context:TurnContext) => {
    // Process bot activity
    await botActivityHandler.run(context);
  });
};

export default httpTrigger;

f:id:horihiro:20201031161106p:plain

はい、怒られました。型が違うようです。さすが typescript。

ここでようやくリファレンスの登場です。

docs.microsoft.com f:id:horihiro:20201031161806p:plain

やっぱり Express (か Restify) ライクなオブジェクトが必要とのこと。

この azure-function-express を使えば、Azure Functions でも Express が使えます。
でも正直ルーティングを必要とするほど HTTP トリガー関数を複数作るわけではないので express の組み込みまではしたくありません。

そこで、azure-function-express からリクエスト/レスポンスのラッパー オブジェクトのみを拝借&BotFrameworkAdapter#adapter が受け取れるよう微修正したテンプレート リポジトリを公開しました。
Express は一切使用しておりません。

github.com

実際にボットとしての動作確認は、Azure で Bot Channels Registration をデプロイして、Teams/LINE で確認しました。

docs.microsoft.com docs.microsoft.com docs.microsoft.com