首页 > 解决方案 > 自定义适配器收到错误:[onTurnError] unhandled error: TypeError: bot.reply is not a function

问题描述

我们创建了一个自定义 Botbuilder 适配器来连接到名为botbuilder-adapter-vonage-js的 Vonage API 。为了测试适配器的基本功能,使用基本的机器人回复,我们向 Vonage 号码发送了一条短信,应该会收到一条短信回复“Hello Back”,但会收到以下错误:

[onTurnError] unhandled error: TypeError: bot.reply is not a function

不确定如何实际调试自定义适配器以找到它被破坏的位置。

很高兴找到熟悉Botkit Core 库Botkit Platform Adapters的人来帮助解决这个问题。我在下面附上了 Express Server (webhook-server.js)。

// webhook-server.js
require('dotenv').config();
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
const SendMessagesAPI = require('./Vonage-SEND-messages-api');
const VonageAdapter = require('botbuilder-adapter-vonage-js');
const Botkit = require('botkit');

const {
  BotFrameworkAdapter,
  InspectionMiddleware,
  MemoryStorage,
  InspectionState,
  UserState,
  ConversationState,
} = require('botbuilder');

const { MicrosoftAppCredentials } = require('botframework-connector');

// This bot's main dialog.
const { IntersectionBot } = require('./bot');
const { Message } = require('@vonage/server-sdk');

const creds = {
  apiKey: process.env.VONAGE_API_KEY,
  apiSecret: process.env.VONAGE_API_SECRET,
  applicationId: process.env.VONAGE_APPLICATION_ID,
  privateKey: process.env.VONAGE_APPLICATION_PRIVATE_KEY_PATH,
};
const config = {
  to_number: process.env.TO_NUMBER,
  from_number: process.env.FROM_NUMBER,
  // enable_incomplete: true
};

// Create Adapter
const adapter = new VonageAdapter(creds, config);

// Create the Storage provider and the various types of BotState.
const memoryStorage = new MemoryStorage();
const inspectionState = new InspectionState(memoryStorage);
const userState = new UserState(memoryStorage);
const conversationState = new ConversationState(memoryStorage);

// Create and add the InspectionMiddleware to the adapter.
adapter.use(
  new InspectionMiddleware(
    inspectionState,
    userState,
    conversationState,
    new MicrosoftAppCredentials(
      process.env.MicrosoftAppId,
      process.env.MicrosoftAppPassword
    )
  )
);

app.post('/webhooks/dlr', (req, res) => {
  res.status(200).end();
});

// Catch-all for errors.
adapter.onTurnError = async (, 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. See https://aka.ms/bottelemetry for telemetry
  //       configuration instructions.
  console.error(`\n [onTurnError] unhandled error: ${error}`);

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

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

// Create the main dialog.
const bot = new IntersectionBot(conversationState, userState);

// Listen for incoming requests.
app.post('/webhooks/inbound', (req, res) => {
  console.log('/webhooks/inbound req.body', req.body);
  adapter.processActivity(req, res, async () => {
    
    console.log(context);
    // [onTurnError] unhandled error: TypeError: Cannot read property 'from' of undefined
    // await bot.run();

     //  [onTurnError] unhandled error: TypeError: .reply is not a function
    // await .reply('I heard a message!');

    // [onTurnError] unhandled error: TypeError: bot.reply is not a function
    await bot.reply('Hello Back!');

  });
  res.status(200).end();
});

app.post('/webhooks/status', (req, res) => {
  res.status(200).end();
});

app.listen(port, () => {
  console.log(` Server running at http://localhost:${port}`);
});

回复

 Server running at http://localhost:3000
/webhooks/inbound req.body {
  message_uuid: 'e93a3007-f7a5-436a-8ba7-c46d64343d80',
  to: { type: 'sms', number: '12018994297' },
  from: { type: 'sms', number: '15754947000' },
  timestamp: '2021-08-27T21:14:51.228Z',
  usage: { price: '0.0057', currency: 'EUR' },
  message: {
    content: { type: 'text', text: 'Hello' },
    sms: { num_messages: '1' }
  },
  direction: 'inbound'
}
TurnContext {
  _respondedRef: { responded: false },
  _turnState: TurnContextStateCollection(2) [Map] {
    'httpStatus' => 200,
    Symbol(state) => { state: [Object], hash: '{}' }
  },
  _onSendActivities: [],
  _onUpdateActivity: [],
  _onDeleteActivity: [],
  _turn: 'turn',
  _locale: 'locale',
  bufferedReplyActivities: [],
  _adapter: VonageAdapter {
    middleware: MiddlewareSet { middleware: [Array] },
    BotIdentityKey: Symbol(BotIdentity),
    OAuthScopeKey: Symbol(OAuthScope),
    name: 'Vonage Adapter',
    middlewares: null,
    botkit_worker: [class VonageBotWorker extends BotWorker],
    credentials: {
      apiKey: '4f2ff535',
      apiSecret: 'jtYzPbh3MXr8M1Hr',
      applicationId: '978500cf-7ea8-4d7b-ac54-2b42f67b28a2',
      privateKey: './private.key'
    },
    options: {},
    to_number: '15754947000',
    from_number: '12018994297',
    enable_incomplete: undefined,
    turnError: [AsyncFunction (anonymous)]
  },
  _activity: {
    id: 'e93a3007-f7a5-436a-8ba7-c46d64343d80',
    timestamp: 2021-08-27T21:14:39.573Z,
    channelId: 'vonage-sms',
    conversation: { id: '15754947000' },
    from: { id: '15754947000' },
    recipient: { id: '12018994297' },
    text: 'Hello',
    channelData: {
      message_uuid: 'e93a3007-f7a5-436a-8ba7-c46d64343d80',
      to: [Object],
      from: [Object],
      timestamp: '2021-08-27T21:14:51.228Z',
      usage: [Object],
      message: [Object],
      direction: 'inbound'
    },
    type: 'message'
  }
}

 [onTurnError] unhandled error: TypeError: bot.reply is not a function

标签: botframeworkbotkit

解决方案


看起来您的自定义适配器是使用 Botkit 和 BotFramework 构建的,类似于其他 Botkit适配器。但是,您的机器人的实现更符合仅为 BotFramework 构建的机器人,但您正在尝试调用reply()属于 Botkit 机器人的方法。

例如,在 Botkit 的“botbuilder-adapter-twilio-sms”适配器中,您会看到两种使用适配器的方法。首先,在Botkit Basics下,创建了一个适配器,然后由 Botkit 使用以形成控制器。然后,这允许您访问reply()可从 Botkit 机器人调用的方法。

其次,在BotBuilder Basics下,创建了一个适配器,然后在 Express 服务器的/api/messages端点中使用该适配器。入站消息被传递到适配器的processActivity()方法,然后机器人使用该方法进行响应,该sendActivity()方法可从 BotFramework 适配器的上下文中调用。

我相信缩小您打算使用的实现范围将减轻您收到的错误。


推荐阅读