在 node.js 和 Bot 框架 V4.4 中调试脚本日志记录中间件

Debugging transcript logging middleware in node.js & Bot framework V4.4

我刚刚按照这个堆栈溢出答案 here.

将我的第一个中间件添加到我的聊天机器人中用于转录记录

但是,执行过程中出现了一些错误,例如:

[onTurnError]: TypeError: this.logger.log is not a function
[onTurnError]: TypeError: Cannot read property 'role' of undefined
BotFrameworkAdapter.processActivity(): 500 ERROR - Error: BotFrameworkAdapter.sendActivity(): missing conversation id.

这些错误是在我初始化对话并且用户发送第一条“GET_STARTED”消息后直接抛出的。

抄本日志记录工作正常,每个(简短!)对话的抄本都发布到我的 Azure Blob 存储。

但是,由于这是我第一次使用中间件,所以我不确定如何调试这些错误并让聊天机器人按预期工作。

记录日志的 onTurn 函数与链接示例非常相似,我只是在末尾添加了一个 await next();

this.onTurn(async (turnContext, next) => {
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
if (turnContext.activity.type === ActivityTypes.Message) {
   if (turnContext.activity.text === '!history') {
      // Retrieve the activities from the Transcript (blob store) and send them over to the channel when a request to upload history arrives. This could be an event or a special message activity as above.
      // Create the connector client to send transcript activities
      let connector = turnContext.adapter.createConnectorClient(turnContext.activity.serviceUrl);

      // Get all the message type activities from the Transcript.
      let continuationToken = '';
      var count = 0;

      // WebChat and Emulator require modifying the activity.Id to display the same activity again within the same chat window
      let updateActivities = [ 'webchat', 'emulator', 'directline' ].includes(turnContext.activity.channelId);
      let incrementId = 0;
      if (updateActivities && turnContext.activity.id.includes('|')) {
                    incrementId = parseInt(turnContext.activity.id.split('|')[1]) || 0;
      } do {
      // Transcript activities are retrieved in pages.  When the continuationToken is null, there are no more activities to be retrieved for this conversation.
      var pagedTranscript = await this.transcriptStore.getTranscriptActivities(turnContext.activity.channelId, turnContext.activity.conversation.id, continuationToken);
      let activities = pagedTranscript.items.filter(item => item.type === ActivityTypes.Message);

         if (updateActivities) {
            activities.forEach(function(a) {
            incrementId++;
            a.id = `${ turnContext.activity.conversation.id }|${ incrementId.toString().padStart(7, '0') }`;
            a.timestamp = new Date().toISOString();
            a.channelData = []; // WebChat uses ChannelData for id comparisons, so we clear it here
            a.replyToId = '';
          });
         }

         // DirectLine only allows the upload of at most 500 activities at a time. The limit of 1500 below is arbitrary and up to the Bot author to decide.
         count += activities.length;
         if (activities.length > 500 || count > 1500) {
            throw new InvalidOperationException('Attempt to upload too many activities');
         }

         await connector.conversations.sendConversationHistory(turnContext.activity.conversation.id, { activities });
                    continuationToken = pagedTranscript.continuationToken;
         }
         while (continuationToken != null);

         console.log("Transcript sent");
         } else {
            // Echo back to the user whatever they typed.
            console.log(`User sent: ${ turnContext.activity.text }\n`);
         }
         } else if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
            // Send greeting when users are added to the conversation.
            console.log('Welcome Message');
         } else {
            // Generic message for all other activities
            console.log(`[${ turnContext.activity.type } event detected]`);
         }
     await next();
 });

下面也是我的 onMessage 函数:

this.onMessage(async (context, next) => {
     await sendTyping(context);
     this.logger.log('Processing a Message Activity');
     // await logMessageText(storageBlob, context);

     const qnaResults = await this.qnaMaker.getAnswers(context);

     // Show choices if the Facebook Payload from ChannelData is not handled
     if (!await fbBot.processFacebookPayload(context, context.activity.channelData)) {
         // detect if Facebook Messenger is the channel being used
         if (context.activity.channelId === 'facebook') {
             let facebookPayload = context.activity.channelData;
             let fbSender = facebookPayload.sender;
             var fbPSID = fbSender.id;
             if (qnaResults[0]) {
                 const { answer, context: { prompts } } = qnaResults[0];

                 // set a variable for the prompts array from QnA
                 var qnaPrompts = null;
                 if (qnaResults[0].context != null) {
                     qnaPrompts = qnaResults[0].context.prompts;
                 }

                 // map the prompts to the required format for quick_replies channelData
                 var qnaPromptsArray = qnaPrompts.map(obj => {
                     return { content_type: 'text', title: obj.displayText, payload: obj.displayText };
                 });

                 let reply;
                 if (prompts.length) {
                     const quickReply = {
                         channelData: {
                         text: answer,
                         quick_replies: qnaPromptsArray
                         }
                     };
                     reply = quickReply;
                 } else {
                     reply = {
                         channelData: {
                         text: answer
                         }
                     };
                 }

                 await context.sendActivity(reply);

             // If no answers were returned from QnA Maker, reply with help.
             } else {
                 let errorCardFB = await fbCards.getErrorCardFB(fbPSID);
                 await context.sendActivity(errorCardFB);
             }
         } else {
         // If an answer was received from QnA Maker, send the answer back to the user.
             if (qnaResults[0]) {
                 const { answer, context: { prompts } } = qnaResults[0];

                 let reply;
                 if (prompts.length) {
                     const card = {
                         'type': 'AdaptiveCard',
                         'body': [
                             {
                             'type': 'TextBlock',
                             'text': answer,
                              wrap: true
                             }
                         ],
                         'actions': prompts.map(({ displayText }) => ({ type: 'Action.Submit', title: displayText, data: displayText })),
                         '$schema': 'http://adaptivecards.io/schemas/adaptive-card.json',
                         'version': '1.1'
                     };

                 reply = { attachments: [CardFactory.adaptiveCard(card)] };
                 } else {
                     reply = answer;
                 }

                 await context.sendActivity(reply);

             // If no answers were returned from QnA Maker, reply with help.
             } else {
                 await context.sendActivity('I\'m sorry, I don\'t have an answer for that. Please ask me something else, such as: \n\n "What Is Mental Health?" \n\n "What Is NeuroDiversity" \n\n "Help"');
             }
         }
     }

     // By calling next() you ensure that the next BotHandler is run.
     await next();
 });

由于我不确定如何调试中间件,并且我使用 Facebook Messenger 作为我的 Chatbot 频道,所以我不确定如何找出导致这些错误的原因。

谁能指出我正确的方向?

如果您打算将转录记录器用作中间件,则需要在 index.js 文件中实现它。中间件附加到您的适配器并且不存在于 "dialog" 代码中。

您的代码应如下所示:

const {
  [...],
  ConsoleTranscriptLogger,
  TranscriptLoggerMiddleware,
  [...]
} = require( 'botbuilder' );

// The bot.
const { WelcomeBot } = require( './bots/welcomeBot' );
const { MainDialog } = require( './dialogs/mainDialog' );

const ENV_FILE = path.join( __dirname, '.env' );
require( 'dotenv' ).config( { path: ENV_FILE } );

// Create HTTP server
let server = restify.createServer();
server.listen( process.env.port || process.env.PORT || 3978, function () {
  console.log( `\n${ server.name } listening to ${ server.url }` );
} );

// configure middleware
const logstore = new ConsoleTranscriptLogger();
const logActivity = new TranscriptLoggerMiddleware( logstore );
[...other middleware...]

// Create adapter.
const adapter = new BotFrameworkAdapter( {
  appId: process.env.MicrosoftAppId,
  appPassword: process.env.MicrosoftAppPassword
} )
  .use( logActivity );

[...other bot code...]

至此,您应该可以开始了。记录器将解析通过适配器传递的每条消息并将其呈现在控制台中。

您可以阅读有关中间件的更多信息here

希望得到帮助!