howdyai / botkit

Botkit is an open source developer tool for building chat bots, apps and custom integrations for major messaging platforms.
MIT License
11.46k stars 2.28k forks source link

Custom Adapter receives error: [onTurnError] unhandled error: TypeError: bot.reply is not a function #2155

Closed KittPhi closed 3 years ago

KittPhi commented 3 years ago

Hello, We've created a Custom Botbuilder Adapter to connect to the Vonage API called botbuilder-adapter-vonage-js. To test the Adapter's basic functionality, with a basic bot reply, we send an sms to the Vonage number and should get a sms reply back "Hello Back", but instead receive the error below.

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

Not sure how to actually debug the Custom Adapter to find where it is broken.

Would be great to find someone familiar with the either the Botkit Core library and Botkit Platform Adapters could help with this. I've attached the Express Server (webhook-server.js) below.

// 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}`);
});

Response

🌏 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
pgoldweic commented 3 years ago

While I'm not sure I can help here myself, I can't help but notice that you're code sample doesn't suggest to me that you've used Botkit core... but likely botbuilder itself. (as I understand it, botbuilder adapters can be used with either botkit OR botbuilder). So you might be asking for help from the wrong crowd? At the least, I suggest you also display what's in bot.js, as it may provide more clarity.