JavaScript v3 ボットを v4 ボットに移行するMigrate a JavaScript v3 bot to a v4 bot

適用対象: SDK v4APPLIES TO: SDK v4

この記事では、v3 SDK JavaScript core-MultiDialogs-v3 ボットを新しい v4 JavaScript ボットに移植する方法について説明します。In this article you will learn how to port the v3 SDK JavaScript core-MultiDialogs-v3 bot to a new v4 JavaScript bot. この変換は次のステージに分けられます。This conversion is broken down into these stages:

  1. 新しいプロジェクトを作成し、依存関係を追加する。Create the new project and add dependencies.
  2. エントリポイントを更新し、定数を定義する。Update the entry point and define constants.
  3. ダイアログを作成し、SDK v4 を使用して再実装します。Create the dialogs and re-implement them using the SDK v4.
  4. ダイアログを実行するようにボットのコードを更新する。Update the bot code to run the dialogs.
  5. store.js ユーティリティ ファイルを移植する。Port the store.js utility file.

このプロセスの最後に、動作する v4 ボットが作成されます。At the end of this process you will have a working v4 bot. 変換後のボットのコピー core-MultiDialogs-v4 も、サンプル リポジトリで入手できます。A copy of the converted bot is also in the samples repo, core-MultiDialogs-v4.

Bot Framework SDK v4 と SDK v3 は、基になる REST API を共有しています。The Bot Framework SDK v4 is based on the same underlying REST API as SDK v3. ただし、SDK v4 は、開発者がより柔軟にボットを制御できるように、以前のバージョンの SDK をリファクタリングしたものです。However, SDK v4 is a refactoring of the previous version of the SDK to allow developers more flexibility and control over their bots. この SDK の主な変更点は次のとおりです。Major changes in the SDK include:

  • 状態は、状態管理オブジェクトとプロパティ アクセサーを使用して管理されます。State is managed via state management objects and property accessors.
  • ターンの処理方法が変更されました。つまり、ボットがユーザーのチャネルから受信アクティビティを受信して応答する方法です。How you handle turns has changed, that is, how the bot receives and responds to an incoming activity from the user's channel.
  • v4 では session オブジェクトを使用しません。代わりに、受信アクティビティに関する情報を格納し、応答アクティビティを返送するために使用できる turn context オブジェクトを使用します。v4 does not use a session object, instead, it has a turn context object that contains information about the incoming activity and can be used to send back a response activity.
  • 新しいダイアログ ライブラリは v3 とは大きく異ります。A new dialogs library that is very different from the one in v3. コンポーネント ダイアログとウォーターフォール ダイアログを使用して、古いダイアログを新しいダイアログ システムに変換する必要があります。You'll need to convert old dialogs to the new dialog system, using component and waterfall dialogs.

注意

移行の一環として、コードの一部をクリーンアップする必要があります。As part of the migration, you also need to clean up some of the code. この記事では、移行プロセスの一環として v3 ロジックに加えた変更について説明します。This article highlights the changes to make to the v3 logic as part of the migration process.

前提条件Prerequisites

このボットについてAbout this bot

移行するボットは、複数のダイアログを使用して会話フローを管理する方法を示しています。The bot you're migrating demonstrates the use of multiple dialogs to manage conversation flow. このボットでは、フライトやホテルの情報を検索できます。The bot can look up flight or hotel information.

  • メインのダイアログでは、どのような種類の情報を探しているのかをユーザーに確認します。The main dialog asks the user what type of information they're looking for.
  • ホテル ダイアログでは、ユーザーに検索パラメーターの入力を求め、モック検索を実行します。The hotel dialog prompts the user for search parameters, and then performs a mock search.
  • フライト ダイアログでは、ボットがキャッチしたエラーが生成され、適切に処理されます。The flight dialog generates an error that the bot catches and deals with gracefully.

新しい v4 ボット プロジェクトを作成して開くCreate and open a new v4 bot project

  1. ボット コードの移植先となる v4 プロジェクトが必要です。You will need a v4 project into which to port the bot code. ローカルでプロジェクトを作成する場合は、「Bot Framework SDK for JavaScript を使用したボットの作成」を参照してください。To create a project locally, see Create a bot with the Bot Framework SDK for JavaScript.

    ヒント

    Azure でプロジェクトを作成することもできます。その場合は、「Azure Bot Service を使用してボットを作成する」を参照してください。You can also create a project on Azure, see Create a bot with Azure Bot Service. ただし、これら 2 つの方法では、若干異なるサポート ファイルが生成されます。However, these two methods result in a slight difference in supporting files. この記事の v4 プロジェクトは、ローカルのプロジェクトとして作成されています。The v4 project for this article was created as a local project.

  2. 次に、Visual Studio Code でプロジェクトを開きます。Then open the project in Visual Studio Code.

package.json ファイルを更新するUpdate the package.json file

  1. Visual Studio Code のターミナル ウィンドウに「npm i botbuilder-dialogs」と入力して、botbuilder-dialogs パッケージに依存関係を追加します。Add a dependency on the botbuilder-dialogs package by entering npm i botbuilder-dialogs in Visual Studio Code's terminal window.

  2. ./package.json を編集し、必要に応じて、nameversiondescription などのプロパティを更新します。Edit ./package.json and update the name, version, description, and other properties as desired.

v4 アプリのエントリ ポイントを更新するUpdate the v4 app entry point

v4 テンプレートで、アプリのエントリ ポイントを表す index.js ファイルと、ボット固有のロジックを表す bot.js ファイルを作成します。The v4 template creates an index.js file for the app entry point and a bot.js file for the bot-specific logic. 後の手順では、後の手順で bot.js ファイルの名前を bots/reservationBot.js に変更し、ダイアログごとにクラスを追加します。In later steps, you will rename the bot.js file to bots/reservationBot.js in a later step and add a class for each dialog.

ボット アプリのエントリ ポイントである ./index.js を編集します。Edit ./index.js, which is the entry point for our bot app. これには、v3 app.js ファイル内の、HTTP サーバーを設定する部分が含まれます。This will contain the portions of the v3 app.js file that set up the HTTP server.

  1. BotFrameworkAdapter に加え、botbuilder パッケージから MemoryStorageConversationState をインポートします。In addition to BotFrameworkAdapter, import MemoryStorage and ConversationState from the botbuilder package. また、ボットおよびメイン ダイアログ モジュールもインポートしますAlso import the bot and main dialog modules. (これらは間もなく作成されますが、ここで参照する必要があります)。(You'll create these soon, but you need to reference them here.)

    // Import required bot services.
    // See https://aka.ms/bot-services to learn more about the different parts of a bot.
    const { BotFrameworkAdapter, MemoryStorage, ConversationState } = require('botbuilder');
    
    // This bot's main dialog.
    const { MainDialog } = require('./dialogs/main')
    const { ReservationBot } = require('./bots/reservationBot');
    
  2. アダプターの onTurnError ハンドラーを定義します。Define an onTurnError handler for the adapter.

    // Catch-all for errors.
    adapter.onTurnError = async (context, error) => {
        const errorMsg = error.message ? error.message : `Oops. Something went wrong!`;
        // This check writes out errors to console log .vs. app insights.
        console.error(`\n [onTurnError]: ${ error }`);
        // Clear out state
        await conversationState.delete(context);
        // Send a message to the user
        await context.sendActivity(errorMsg);
    };
    

    v4 では、ボット アダプターを 使用して受信 アクティビティをボットにルーティングします。In v4, you use a bot adapter to route incoming activities to the bot. アダプターを使用すると、ターンが終了する前に、エラーをキャッチして対処することができます。The adapter allows us to catch and react to errors before a turn finishes. ここでは、アプリケーション エラーが発生した場合に会話状態をクリアします。この場合、すべてのダイアログがリセットされ、ボットが破損した会話状態の状態を維持します。Here, you clear conversation state if an application error occurs, which will reset all dialogs and keep the bot from staying in a corrupted conversation state.

  3. ボットを作成するためのテンプレート コードを以下で置き換えます。Replace the template code for creating the bot with this.

    // Define state store for your bot.
    const memoryStorage = new MemoryStorage();
    
    // Create conversation state with in-memory storage provider.
    const conversationState = new ConversationState(memoryStorage);
    
    // Create the base dialog and bot
    const dialog = new MainDialog();
    const reservationBot = new ReservationBot(conversationState, dialog);
    

    インメモリ ストレージ レイヤーは クラスによって提供され、会話状態管理オブジェクトを明示的に作成 MemoryStorage する必要があります。The in-memory storage layer is now provided by the MemoryStorage class, and you need to explicitly create a conversation state management object.

    ダイアログ定義コードは、まもなく MainDialog 定義するクラスに移動されました。The dialog definition code has been moved to a MainDialog class that you'll define shortly. また、ボット定義コードを クラスに移行 ReservationBot します。you'll also migrate the bot definition code into a ReservationBot class.

  4. 最後に、アダプターを使用してアクティビティをボットにルーティングするために、サーバーの要求ハンドラーを更新します。Finally, you update the server's request handler to use the adapter to route activities to the bot.

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

    v4 では、ボットは から派生します。これは、ターンのアクティビティを受け取 ActivityHandler run るメソッドを定義します。In v4, the bot derives from ActivityHandler, which defines the run method to receive an activity for a turn.

定数ファイルを追加するAdd a constants file

ボットの 識別子を保持const.js./const.js ファイルを作成します。Create a ./const.js file to hold identifiers for your bot.

module.exports = {
    MAIN_DIALOG: 'mainDialog',
    INITIAL_PROMPT: 'initialPrompt',
    HOTELS_DIALOG: 'hotelsDialog',
    INITIAL_HOTEL_PROMPT: 'initialHotelPrompt',
    CHECKIN_DATETIME_PROMPT: 'checkinTimePrompt',
    HOW_MANY_NIGHTS_PROMPT: 'howManyNightsPrompt',
    FLIGHTS_DIALOG: 'flightsDialog',
};

v4 では、ダイアログ オブジェクトとプロンプト オブジェクトに ID が割り当てられ、ダイアログとプロンプトは ID で呼び出されます。In v4, IDs are assigned to dialog and prompt objects, and the dialogs and prompts are invoked by ID.

新しいダイアログ ファイルを作成するCreate new dialog files

以下のファイルを作成します。Create these files:

ファイル名File name 説明Description
./dialogs/flights.js./dialogs/flights.js hotels ダイアログの移行済みのロジックが含まれます。This will contain the migrated logic for the hotels dialog.
./dialogs/hotels.js./dialogs/hotels.js flights ダイアログの移行済みのロジックが含まれます。This will contain the migrated logic for the flights dialog.
./dialogs/main.js./dialogs/main.js ボットの移行済みのロジックが含まれ、root ダイアログの代わりとして機能します。This will contain the migrated logic for our bot, and will stand in for the root dialog.

サポート ダイアログは移行していません。We have not migrated the support dialog. v4 でヘルプ ダイアログを実装する方法の例については、「ユーザーによる割り込みを処理する」を参照してください。For an example of how to implement a help dialog in v4, see Handle user interruptions.

メイン ダイアログを実装するImplement the main dialog

v3 では、すべてのボットがダイアログ システムの上に構築されました。In v3, all bots were built on top of a dialog system. v4 では、ボット ロジックとダイアログ ロジックが分離されました。In v4, bot logic and dialog logic is now separate. v3 ボットのルート ダイアログを 取得し、その代わるクラス MainDialog を作成しました。You've taken what was the root dialog in the v3 bot and made a MainDialog class to take its place.

./dialogs/main.js を編集します。Edit ./dialogs/main.js.

  1. ダイアログに必要なクラスと定数をインポートします。Import the classes and constants you need for the dialog.

    const { DialogSet, DialogTurnStatus, ComponentDialog, WaterfallDialog,
        ChoicePrompt } = require('botbuilder-dialogs');
    const { FlightDialog } = require('./flights');
    const { HotelsDialog } = require('./hotels');
    const { MAIN_DIALOG,
        INITIAL_PROMPT,
        HOTELS_DIALOG,
        FLIGHTS_DIALOG
    } = require('../const');
    
  2. MainDialog クラスを定義してエクスポートします。Define and export the MainDialog class.

    const initialId = 'mainWaterfallDialog';
    
    class MainDialog extends ComponentDialog {
        constructor() {
            super(MAIN_DIALOG);
    
            // Create a dialog set for the bot. It requires a DialogState accessor, with which
            // to retrieve the dialog state from the turn context.
            this.addDialog(new ChoicePrompt(INITIAL_PROMPT, this.validateNumberOfAttempts.bind(this)));
            this.addDialog(new FlightDialog(FLIGHTS_DIALOG));
    
            // Define the steps of the base waterfall dialog and add it to the set.
            this.addDialog(new WaterfallDialog(initialId, [
                this.promptForBaseChoice.bind(this),
                this.respondToBaseChoice.bind(this)
            ]));
    
            // Define the steps of the hotels waterfall dialog and add it to the set.
            this.addDialog(new HotelsDialog(HOTELS_DIALOG));
    
            this.initialDialogId = initialId;
        }
    }
    
    module.exports.MainDialog = MainDialog;
    

    これにより、メイン ダイアログが直接参照する、他のダイアログとプロンプトが宣言されます。This declares the other dialogs and prompts that the main dialog references directly.

    • このダイアログのステップを含むメイン ウォーターフォール ダイアログ。The main waterfall dialog that contains the steps for this dialog. コンポーネント ダイアログが開始されると、これによって "初期ダイアログ" が開始されます。When the component dialog starts, it starts its initial dialog.
    • 実行するタスクをユーザーに要求するために使用するオプションプロンプト。The choice prompt that you'll use to ask the user which task they'd like to perform. バリデーターを使用して choice プロンプトを作成しました。You've created the choice prompt with a validator.
    • 2 つの子ダイアログ (フライトとホテル)。The two child dialogs, flights and hotels.
  3. run ヘルパー メソッドをクラスに追加します。Add a run helper method to the class.

    /**
     * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system.
     * If no dialog is active, it will start the default dialog.
     * @param {*} turnContext
     * @param {*} accessor
     */
    async run(turnContext, accessor) {
        const dialogSet = new DialogSet(accessor);
        dialogSet.add(this);
    
        const dialogContext = await dialogSet.createContext(turnContext);
        const results = await dialogContext.continueDialog();
        if (results.status === DialogTurnStatus.empty) {
            await dialogContext.beginDialog(this.id);
        }
    }
    

    v4 では、ボットは、まずダイアログ コンテキストを作成し、次に continueDialog を呼び出すことによって、ダイアログ システムとやり取りします。In v4, a bot interacts with the dialog system by creating a dialog context first, and then calling continueDialog. アクティブなダイアログが存在する場合、これに制御が渡されます。それ以外の場合、単にこの呼び出しから制御が戻ります。If there is an active dialog, control is passed to it; otherwise, this call simply returns. の結果は、 empty アクティブなダイアログがないことを示しているので、ここではメインダイアログを再び開始します。A result of empty indicates that no dialog was active, and so here, you start the main dialog again.

    accessor パラメーターによって、ダイアログの状態プロパティのアクセサーが渡されます。The accessor parameter passes in the accessor for the dialog state property. "ダイアログ スタック" の状態は、このプロパティに格納されます。State for the dialog stack is stored in this property. v4 における状態とダイアログのしくみの詳細については、それぞれ「状態の管理」と「ダイアログ ライブラリ」を参照してください。For more information about how state and dialogs work in v4, see Managing state and Dialogs library, respectively.

  4. メイン ダイアログのウォーターフォール ステップと、選択プロンプトの検証コントロールをクラスに追加します。To the class, add the waterfall steps of the main dialog and the validator for the choice prompt.

    async promptForBaseChoice(stepContext) {
        return await stepContext.prompt(
            INITIAL_PROMPT, {
                prompt: 'Are you looking for a flight or a hotel?',
                choices: ['Hotel', 'Flight'],
                retryPrompt: 'Not a valid option'
            }
        );
    }
    
    async respondToBaseChoice(stepContext) {
        // Retrieve the user input.
        const answer = stepContext.result.value;
        if (!answer) {
            // exhausted attempts and no selection, start over
            await stepContext.context.sendActivity('Not a valid option. We\'ll restart the dialog ' +
                'so you can try again!');
            return await stepContext.endDialog();
        }
        if (answer === 'Hotel') {
            return await stepContext.beginDialog(HOTELS_DIALOG);
        }
        if (answer === 'Flight') {
            return await stepContext.beginDialog(FLIGHTS_DIALOG);
        }
        return await stepContext.endDialog();
    }
    
    async validateNumberOfAttempts(promptContext) {
        if (promptContext.attemptCount > 3) {
            // cancel everything
            await promptContext.context.sendActivity('Oops! Too many attempts :( But don\'t worry, I\'m ' +
                'handling that exception and you can try again!');
            return await promptContext.context.endDialog();
        }
    
        if (!promptContext.recognized.succeeded) {
            await promptContext.context.sendActivity(promptContext.options.retryPrompt);
            return false;
        }
        return true;
    }
    

    ウォーターフォールの最初のステップでは、選択プロンプトを開始してユーザーに選択するよう求めます。これ自体がダイアログです。The first step of the waterfall asks the user to make a choice, by starting the choice prompt, which is itself a dialog. ウォーターフォールの 2 番目のステップでは、選択プロンプトの結果が使用されます。The second step of the waterfall consumes the result of the choice prompt. 子ダイアログが開始されるか (選択が行われた場合)、メイン ダイアログが終了されます (ユーザーが選択しなかった場合)。It either starts a child dialog (if a choice was made) or ends the main dialog (if the user failed to make a choice).

    ユーザーが有効な選択を行った場合、選択プロンプトはユーザーの選択内容を返すか、もう一度選択するようユーザーに再プロンプトを出します。The choice prompt will either return the user's choice, if they made a valid choice, or reprompt the user to make the choice again. 検証コントロールは、ユーザーに対して何回続けてプロンプトが出されたかをチェックし、試行が 3 回失敗したらプロンプトの失敗を許可します。この場合、メインのウォーターフォール ダイアログに制御が戻されます。The validator checks how many times in a row the prompt has been made to the user and allows the prompt to fail out after 3 failed attempts, returning control to the main waterfall dialog.

フライト ダイアログを実装するImplement the flights dialog

v3 のボットでは、フライト ダイアログは、ボットが会話のエラーを処理する方法を示すスタブでした。In the v3 bot, the flights dialog was a stub that demonstrated how the bot handles a conversation error. ここでは、同じことを行います。Here, you do the same.

./dialogs/flights.js を編集します。Edit ./dialogs/flights.js.

const { ComponentDialog, WaterfallDialog } = require('botbuilder-dialogs');

const initialId = 'flightsWaterfallDialog';

class FlightDialog extends ComponentDialog {
    constructor(id) {
        super(id);

        // ID of the child dialog that should be started anytime the component is started.
        this.initialDialogId = initialId;

        // Define the conversation flow using a waterfall model.
        this.addDialog(new WaterfallDialog(initialId, [
            async () => {
                throw new Error('Flights Dialog is not implemented and is instead ' +
                    'being used to show Bot error handling');
            }
        ]));
    }
}

exports.FlightDialog = FlightDialog;

ホテル ダイアログを実装するImplement the hotels dialog

ホテルのダイアログと同じ全体のフローを保持します。宛先をたずねる、日付を要求する、夜間の数を確認し、検索条件に一致したオプションの一覧をユーザーに表示します。You keep the same overall flow of the hotel dialog: ask for a destination, ask for a date, ask for the number of nights to stay, and then show the user a list of options that matched their search.

./dialogs/hotels.js を編集します。Edit ./dialogs/hotels.js.

  1. ダイアログに必要なクラスと定数をインポートします。Import the classes and constants you'll need for the dialog.

    const { ComponentDialog, WaterfallDialog, TextPrompt, DateTimePrompt } = require('botbuilder-dialogs');
    const { AttachmentLayoutTypes, CardFactory } = require('botbuilder');
    const store = require('../store');
    const {
        INITIAL_HOTEL_PROMPT,
        CHECKIN_DATETIME_PROMPT,
        HOW_MANY_NIGHTS_PROMPT
    } = require('../const');
    
  2. HotelsDialog クラスを定義してエクスポートします。Define and export the HotelsDialog class.

    const initialId = 'hotelsWaterfallDialog';
    
    class HotelsDialog extends ComponentDialog {
        constructor(id) {
            super(id);
    
            // ID of the child dialog that should be started anytime the component is started.
            this.initialDialogId = initialId;
    
            // Register dialogs
            this.addDialog(new TextPrompt(INITIAL_HOTEL_PROMPT));
            this.addDialog(new DateTimePrompt(CHECKIN_DATETIME_PROMPT));
            this.addDialog(new TextPrompt(HOW_MANY_NIGHTS_PROMPT));
    
            // Define the conversation flow using a waterfall model.
            this.addDialog(new WaterfallDialog(initialId, [
                this.destinationPromptStep.bind(this),
                this.destinationSearchStep.bind(this),
                this.checkinPromptStep.bind(this),
                this.checkinTimeSetStep.bind(this),
                this.stayDurationPromptStep.bind(this),
                this.stayDurationSetStep.bind(this),
                this.hotelSearchStep.bind(this)
            ]));
        }
    }
    
    exports.HotelsDialog = HotelsDialog;
    
  3. クラスに、ダイアログの手順で使用するいくつかのヘルパー関数を追加します。To the class, add a couple of helper functions that you'll use in the dialog steps.

    addDays(startDate, days) {
        const date = new Date(startDate);
        date.setDate(date.getDate() + days);
        return date;
    };
    
    createHotelHeroCard(hotel) {
        return CardFactory.heroCard(
            hotel.name,
            `${hotel.rating} stars. ${hotel.numberOfReviews} reviews. From ${hotel.priceStarting} per night.`,
            CardFactory.images([hotel.image]),
            CardFactory.actions([
                {
                    type: 'openUrl',
                    title: 'More details',
                    value: `https://www.bing.com/search?q=hotels+in+${encodeURIComponent(hotel.location)}`
                }
            ])
        );
    }
    

    createHotelHeroCard によって、ホテルに関する情報を含むヒーロー カードが作成されます。createHotelHeroCard creates a hero card containing information about a hotel.

  4. ダイアログで使用するウォーターフォール ステップをクラスに追加します。To the class, add the waterfall steps used in the dialog.

    async destinationPromptStep(stepContext) {
        await stepContext.context.sendActivity('Welcome to the Hotels finder!');
        return await stepContext.prompt(
            INITIAL_HOTEL_PROMPT, {
                prompt: 'Please enter your destination'
            }
        );
    }
    
    async destinationSearchStep(stepContext) {
        const destination = stepContext.result;
        stepContext.values.destination = destination;
        await stepContext.context.sendActivity(`Looking for hotels in ${destination}`);
        return stepContext.next();
    }
    
    async checkinPromptStep(stepContext) {
        return await stepContext.prompt(
            CHECKIN_DATETIME_PROMPT, {
                prompt: 'When do you want to check in?'
            }
        );
    }
    
    async checkinTimeSetStep(stepContext) {
        const checkinTime = stepContext.result[0].value;
        stepContext.values.checkinTime = checkinTime;
        return stepContext.next();
    }
    
    async stayDurationPromptStep(stepContext) {
        return await stepContext.prompt(
            HOW_MANY_NIGHTS_PROMPT, {
                prompt: 'How many nights do you want to stay?'
            }
        );
    }
    
    async stayDurationSetStep(stepContext) {
        const numberOfNights = stepContext.result;
        stepContext.values.numberOfNights = parseInt(numberOfNights);
        return stepContext.next();
    }
    
    async hotelSearchStep(stepContext) {
        const destination = stepContext.values.destination;
        const checkIn = new Date(stepContext.values.checkinTime);
        const checkOut = this.addDays(checkIn, stepContext.values.numberOfNights);
    
        await stepContext.context.sendActivity(`Ok. Searching for Hotels in ${destination} from 
            ${checkIn.toDateString()} to ${checkOut.toDateString()}...`);
        const hotels = await store.searchHotels(destination, checkIn, checkOut);
        await stepContext.context.sendActivity(`I found in total ${hotels.length} hotels for your dates:`);
    
        const hotelHeroCards = hotels.map(this.createHotelHeroCard);
    
        await stepContext.context.sendActivity({
            attachments: hotelHeroCards,
            attachmentLayout: AttachmentLayoutTypes.Carousel
        });
    
        return await stepContext.endDialog();
    }
    

    V3 ホテルダイアログの手順を、v4 ホテルダイアログのウォーターフォールの手順に移行しました。You've migrated the steps from the v3 hotels dialog into the waterfall steps of the v4 hotels dialog.

ボットを更新するUpdate the bot

v4 のボットでは、ダイアログ システム外部のアクティビティに対処できます。In v4, bots can react to activities outside of the dialog system. ActivityHandler クラスでは一般的な種類のアクティビティに対応したハンドラーを定義できるため、コードの管理が容易になります。The ActivityHandler class defines handlers for common types of activities, to make it easier to manage your code.

./bot.js の名前を ./bots/reservationBot.js に変更し、これを編集します。Rename ./bot.js to ./bots/reservationBot.js, and edit it.

  1. このファイルには、ボットの基本実装を提供する ActivityHandler が既にインポートされています。The file already imports the ActivityHandler, which provides a base implementation of a bot.

    const { ActivityHandler } = require('botbuilder');
    
  2. クラスの名前を ReservationBot に変更します。Rename the class to ReservationBot.

    class ReservationBot extends ActivityHandler {
        // ...
    }
    
    module.exports.ReservationBot = ReservationBot;
    
  3. 受信しているオブジェクトを受け入れるようにコンストラクターの署名を更新します。Update the signature of the constructor, to accept the objects you're receiving.

    /**
     *
     * @param {ConversationState} conversationState
     * @param {Dialog} dialog
     * @param {any} logger object for logging events, defaults to console if none is provided
    */
    constructor(conversationState, dialog, logger) {
        super();
        // ...
    }
    
  4. コンストラクターでは、null パラメーターのチェックを追加し、クラス コンストラクターのプロパティを定義します。In the constructor, add null parameter checks and define class constructor properties.

    if (!conversationState) throw new Error('[DialogBot]: Missing parameter. conversationState is required');
    if (!dialog) throw new Error('[DialogBot]: Missing parameter. dialog is required');
    if (!logger) {
        logger = console;
        logger.log('[DialogBot]: logger not passed in, defaulting to console');
    }
    
    this.conversationState = conversationState;
    this.dialog = dialog;
    this.logger = logger;
    this.dialogState = this.conversationState.createProperty('DialogState');
    

    ここでは、ダイアログスタックの状態を格納するダイアログ状態プロパティアクセサーを作成します。This is where you create the dialog state property accessor that will store state for the dialog stack.

  5. コンストラクターで、onMessage ハンドラーを更新し、onDialog ハンドラーを追加します。In the constructor, update the onMessage handler and add an onDialog handler.

    this.onMessage(async (context, next) => {
        this.logger.log('Running dialog with Message Activity.');
    
        // Run the Dialog with the new message Activity.
        await this.dialog.run(context, this.dialogState);
    
        // By calling next() you ensure that the next BotHandler is run.
        await next();
    });
    
    this.onDialog(async (context, next) => {
        // Save any state changes. The load happened during the execution of the Dialog.
        await this.conversationState.saveChanges(context, false);
    
        // By calling next() you ensure that the next BotHandler is run.
        await next();
    });
    

    ActivityHandler によってメッセージ アクティビティが onMessage にルーティングされます。The ActivityHandler routes message activities to onMessage. このボットは、ダイアログを介してすべてのユーザー入力を処理します。This bot handles all user input via dialogs.

    ActivityHandler は、ターンが終了して制御がアダプターに戻る前に onDialog を呼び出します。The ActivityHandler calls onDialog at the end of the turn, before returning control to the adapter. ターンを終了する前に、状態を明示的に保存する必要があります。You need to explicitly save state before exiting the turn. そうしないと、状態の変更が保存されないため、ダイアログが正しく動作しません。Otherwise, the state changes will not get saved and the dialog will not run properly.

  6. 最後に、コンストラクターで onMembersAdded ハンドラーを更新します。Finally, update the onMembersAdded handler in the constructor.

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

    ActivityHandler は、ボット以外の参加者が会話に追加されたことを示す会話更新アクティビティを受け取ると、onMembersAdded を呼び出します。The ActivityHandler calls onMembersAdded when it receives a conversation update activity that indicates participants other than the bot were added to the conversation. このメソッドを更新して、ユーザーがメッセージ交換に参加したときに案内メッセージを送信します。You update this method to send a greeting message when a user joins the conversation.

ストア ファイルを作成するCreate the store file

ホテル ダイアログで使用される ./store.js ファイルを作成します。Create the ./store.js file, used by the hotels dialog. searchHotels はモックのホテル検索関数で、v3 ボットと同じです。searchHotels is a mock hotel search function, same as in the v3 bot.

module.exports = {
    searchHotels: destination => {
        return new Promise(resolve => {

            // Filling the hotels results manually just for demo purposes
            const hotels = [];
            for (let i = 1; i <= 5; i++) {
                hotels.push({
                    name: `${destination} Hotel ${i}`,
                    location: destination,
                    rating: Math.ceil(Math.random() * 5),
                    numberOfReviews: Math.floor(Math.random() * 5000) + 1,
                    priceStarting: Math.floor(Math.random() * 450) + 80,
                    image: `https://placeholdit.imgix.net/~text?txtsize=35&txt=Hotel${i}&w=500&h=260`
                });
            }

            hotels.sort((a, b) => a.priceStarting - b.priceStarting);

            // complete promise with a timer to simulate async response
            setTimeout(() => { resolve(hotels); }, 1000);
        });
    }
};

エミュレーターでボットをテストするTest the bot in the Emulator

この時点で、bot をローカルで実行し、エミュレーターでアタッチできます。At this point, you should be able to run the bot locally and attach to it with the Emulator.

  1. ご自身のマシンを使ってローカルでサンプルを実行します。Run the sample locally on your machine. Visual Studio Code でデバッグ セッションを開始した場合、ボットをテストすると、ログ情報はデバッグ コンソールに送信されます。If you start a debugging session in Visual Studio Code, logging information is sent to the debug console as you test the bot.
  2. エミュレーターを起動し、bot に接続します。Start the Emulator and connect to the bot.
  3. メッセージを送信して、メイン、フライト、ホテルの各ダイアログをテストします。Send messages to test the main, flight, and hotel dialogs.

その他のリソースAdditional resources

v4 の概念トピックv4 conceptual topics:

v4 の使用方法のトピックv4 how-to topics: