Closed seanbetts closed 4 years ago
@seanbetts, the current version of Botbuilder is v4.5.3. Have you tried updating the package and testing? In the mean time, I will attempt to repro here and will let you know what I find.
Also, have you noticed, is it any trace activity or specific to QnA?
I'm unable to repro the issue as I am only receiving one QnA trace log over Facebook. I tested with v4.4.0 as well as v4.5.3.
Can you post any relevant code so I can better diagnose? I'm interested in how you are calling the transcript logger, how state is being instantiated, used, and saved, and where values are being passed into QnA and returned in your dialog(s).
Hi @stevkan - thank you for looking into this. Below is a copy of my index.js file that shows how I'm calling the transcript logger:
// Import required pckages
const path = require('path');
const restify = require('restify');
// Import required bot services.
const { BotFrameworkAdapter, MemoryStorage, ConversationState, UserState } = require('botbuilder');
const { BotConfiguration } = require('botframework-config');
// Import middleware for transcript logging
const { TranscriptLoggerMiddleware } = require('botbuilder-core'); // ConsoleTranscriptLogger,
// Import storage for transcript
const { AzureBlobTranscriptStore } = require('botbuilder-azure');
// Enable Application Insights
const appInsights = require('applicationinsights');
appInsights.setup('b91c8f19-39fc-4b5d-a7e5-b9cf26a4030d')
.setUseDiskRetryCaching(true)
.start();
// This bot's main dialog.
const { DialogBot } = require('./bots/dialogBot');
// Read botFilePath and botFileSecret from .env file
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Get the .bot file path
const BOT_FILE = path.join(__dirname, (process.env.botFilePath || ''));
let botConfig;
try {
// Read bot configuration from .bot file.
botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret);
} catch (err) {
console.error(`Error reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`);
process.exit();
}
// For local development configuration as defined in .bot file
const DEV_ENVIRONMENT = 'development';
// Name of Transcript Blob Type service within the .bot file
const BLOB_TRANSCRIPT_CONFIGURATION = 'BlobTranscriptStorage';
// Name of Console Logs Blob Type service within the .bot file
// const BLOB_CONSOLE_LOGS_CONFIGURATION = 'BlobConsoleLogStorage';
// Define name of the endpoint configuration section from the .bot file
const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT);
// Get bot endpoint configuration by service name - Bot configuration as defined in .bot file
const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION);
// Create bot adapter
const adapter = new BotFrameworkAdapter({
appId: process.env.microsoftAppId, // || endpointConfig.appId,
appPassword: process.env.microsoftAppPassword // || endpointConfig.appPassword
});
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError]: ${ error }`);
// Send a message to the user
await context.sendActivity(`😬/nOops. Something went wrong! Please try asking me something else.`);
// Clear out state
await conversationState.delete(context);
};
// Get blob service configuration for transcripts and console logs as defined in .bot file
const blobTranscriptStorageConfig = botConfig.findServiceByNameOrId(BLOB_TRANSCRIPT_CONFIGURATION);
// const blobConsoleLogsStorageConfig = botConfig.findServiceByNameOrId(BLOB_CONSOLE_LOGS_CONFIGURATION);
// The transcript store has methods for saving and retrieving bot conversation transcripts.
let transcriptStore = new AzureBlobTranscriptStore({
storageAccountOrConnectionString: blobTranscriptStorageConfig.connectionString,
containerName: blobTranscriptStorageConfig.container
});
// Create the middleware layer responsible for logging incoming and outgoing activities into the transcript store.
var transcriptMiddleware = new TranscriptLoggerMiddleware(transcriptStore);
adapter.use(transcriptMiddleware);
// The transcript store has methods for saving and retrieving bot console logs.
// let consoleLogStore = new AzureBlobTranscriptStore({
// storageAccountOrConnectionString: blobConsoleLogsStorageConfig.connectionString,
// containerName: blobConsoleLogsStorageConfig.container
// });
// Create the middleware layer responsible for logging console messages in the transcript store
// var consoleLogMiddleware = new ConsoleTranscriptLogger(consoleLogStore);
// adapter.use(consoleLogMiddleware);
// Define state store for your bot.
// See https://aka.ms/about-bot-state to learn more about bot state.
const memoryStorage = new MemoryStorage();
// Create conversation and user state with in-memory storage provider.
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);
// CAUTION: You must ensure your product environment has the NODE_ENV set
// to use the Azure Blob storage or Azure Cosmos DB providers.
// Add botbuilder-azure when using any Azure services.
// const { BlobStorage } = require('botbuilder-azure');
// Get service configuration
// const blobStorageConfig = botConfig.findServiceByNameOrId(STORAGE_CONFIGURATION_ID);
// const blobStorage = new BlobStorage({
// containerName: (blobStorageConfig.container || DEFAULT_BOT_CONTAINER),
// storageAccountOrConnectionString: blobStorageConfig.connectionString,
// });
// conversationState = new ConversationState(blobStorage);
// userState = new UserState(blobStorage);
// Pass in a logger to the bot. For this sample, the logger is the console, but alternatives such as Application Insights and Event Hub exist for storing the logs of the bot.
const logger = console;
// Create the main dialog.
// const dialog = new UserProfileDialog(userState, logger);
const bot = new DialogBot(conversationState, userState, logger, transcriptStore);
// 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 }`);
console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`);
});
// Listen for incoming activities and route them to your bot main dialog.
server.post('/api/messages', (req, res) => {
// Route received a request to adapter for processing
adapter.processActivity(req, res, async (turnContext) => {
// route to bot activity handler.
await bot.run(turnContext);
});
});
Below is my dialogBot.js that manages the dialog:
const { ActivityHandler, ActivityTypes } = require('botbuilder');
const { CardFactory } = require('botbuilder-core');
const { QnAMaker } = require('botbuilder-ai');
// const CONVERSATION_DATA_PROPERTY = 'conversationData';
// const USER_PROFILE_PROPERTY = 'userProfile';
const WelcomeCard = require('../resources/welcomeCard.json');
const fbCards = require('../resources/fbCards.js');
const fbBot = require('../bots/fbBot.js');
// const teamsWelcomeCard = require('../resources/teamsWelcomeCard.json');
// initialized to access values in .env file.
var dotenv = require('dotenv');
dotenv.config();
var sendTyping = async function(context) {
await context.sendActivities([
{ type: 'typing' },
{ type: 'delay', value: 2000 }
]);
};
class DialogBot extends ActivityHandler {
constructor(conversationState, userState, dialog, logger) {
super();
// Create the state property accessors for the conversation data and user profile.
// this.conversationData = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// this.userProfile = userState.createProperty(USER_PROFILE_PROPERTY);
// The state management objects for the conversation and user state.
// this.conversationState = conversationState;
// this.userState = userState;
if (!conversationState) throw new Error('[DialogBot]: Missing parameter. conversationState is required');
if (!userState) throw new Error('[DialogBot]: Missing parameter. userState 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');
}
try {
this.qnaMaker = new QnAMaker({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
endpointKey: process.env.QnAAuthKey,
host: process.env.QnAEndpointHostName
});
} catch (err) {
logger.warn(`QnAMaker Exception: ${ err } Check your QnAMaker configuration in .env`);
}
this.conversationState = conversationState;
this.userState = userState;
this.dialog = dialog;
this.logger = logger;
this.dialogState = this.conversationState.createProperty('DialogState');
// If a new user is added to the conversation, send them a greeting message
this.onMembersAdded(async (context, next) => {
await sendTyping(context);
const membersAdded = context.activity.membersAdded;
for (let cnt = 0; cnt < membersAdded.length; cnt++) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
const welcomeCard = CardFactory.adaptiveCard(WelcomeCard);
await context.sendActivity({ attachments: [welcomeCard] });
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
// When a user sends a message, perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
this.onMessage(async (context, next) => {
await sendTyping(context);
console.log('Processing a Message Activity');
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();
});
this.onEvent(async (turnContext) => {
console.log('Processing an Event Activity.');
// Analyze Facebook payload from EventActivity.Value
await fbBot.processFacebookPayload(turnContext, turnContext.activity.value);
});
this.onDialog(async (context, next) => {
// Save any state changes. The load happened during the execution of the Dialog.
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
await next();
});
}
}
module.exports.DialogBot = DialogBot;
Finally, below is the customised code for the Facebook channel:
const { QnAMaker } = require('botbuilder-ai');
const fbCards = require('../resources/fbCards.js');
var qnaMaker = new QnAMaker({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
endpointKey: process.env.QnAAuthKey,
host: process.env.QnAEndpointHostName
});
// Customisation code for Facebook Messenger
// Process a Facebook payload from channel data, to handle optin events, postbacks and quick replies.
const processFacebookPayload = async function(turnContext, data) {
// At this point we know we are on Facebook channel, and can consume the Facebook custom payload present in channelData.
const facebookPayload = data;
if (facebookPayload) {
if (facebookPayload.postback) {
// Postback
await this.onFacebookPostback(turnContext, facebookPayload.postback, facebookPayload.sender);
return true;
} else if (facebookPayload.optin) {
// Optin
await this.onFacebookOptin(turnContext, facebookPayload.optin);
return true;
} else if (facebookPayload.message && facebookPayload.message.quick_reply) {
// Quick Reply
await this.onFacebookQuickReply(turnContext, facebookPayload.message.quick_reply, facebookPayload.sender);
return true;
}
}
return false;
};
// Called when receiving a Facebook messaging_postback event. Facebook Developer Reference: Postback - https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_postbacks/
const onFacebookPostback = async function(turnContext, postback, sender) {
console.log('Facebook Postback message received.');
var fbPSID = sender.id;
// PostBack handling logic
if (postback.payload === 'GET_STARTED') {
let welcomeCardFB = await fbCards.getWelcomeCardFB(fbPSID);
await turnContext.sendActivity(welcomeCardFB);
} else {
if (postback.payload === 'GET_HELP') {
let helpCardFB = await fbCards.getHelpCardFB(fbPSID);
await turnContext.sendActivity(helpCardFB);
} else {
if (postback.payload === 'WHAT_IS_MENTAL_HEALTH') {
// qnaMaker.getAnswers doesn't accept string input, so we need to adjust our turnContext
// to match what it expects, which is a string in Activity.Text
turnContext.activity.text = postback.payload;
// call the QnA
const qnaResults = await qnaMaker.getAnswers(turnContext);
// If an answer was received from QnA Maker, send the answer back to the user.
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 turnContext.sendActivity(reply);
// If no answers were returned from QnA Maker, reply with help.
} else {
await turnContext.sendActivity('No QnA Maker answers were found.');
}
} else {
if (postback.payload === 'WHAT_IS_NEURODIVERSITY') {
// qnaMaker.getAnswers doesn't accept string input, so we need to adjust our turnContext
// to match what it expects, which is a string in Activity.Text
turnContext.activity.text = postback.payload;
// call the QnA
const qnaResults = await qnaMaker.getAnswers(turnContext);
// If an answer was received from QnA Maker, send the answer back to the user.
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 turnContext.sendActivity(reply);
// If no answers were returned from QnA Maker, reply with help.
} else {
let errorCardFB = await fbCards.getErrorCardFB(fbPSID);
await turnContext.sendActivity(errorCardFB);
}
} else {
if (postback.payload === 'TEXT_SHOUT') {
await turnContext.sendActivity(fbCards.shoutCardFB);
} else {
if (postback.payload === 'TEXT_SHOUT_NOW') {
await turnContext.sendActivity('To get help from Shout please text SHOUT to 85258');
} else {
if (postback.payload === 'CALL_THE_NHS') {
await turnContext.sendActivity(fbCards.nhsCardFB);
} else {
if (postback.payload === 'CALL_THE_SAMARITANS') {
await turnContext.sendActivity(fbCards.samaritansCardFB);
} else {
await turnContext.sendActivity('Facebook PostBack not defined...YET!');
}
}
}
}
}
}
}
}
};
// Called when receiving a Facebook messaging_optin event. Facebook Developer Reference: Optin - https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_optins/
const onFacebookOptin = async function(turnContext, optin) {
console.log('Optin message received.');
// TODO: Your optin handling logic here...
};
// Called when receiving a Facebook quick reply. Facebook Developer Reference: Quick reply - https://developers.facebook.com/docs/messenger-platform/send-messages/quick-replies/
const onFacebookQuickReply = async function(turnContext, quickReply, sender) {
console.log('Facebook QuickReply message received.');
var fbPSID = sender.id;
// qnaMaker.getAnswers doesn't accept string input, so we need to adjust our turnContext
// to match what it expects, which is a string in Activity.Text
turnContext.activity.text = quickReply.payload;
// call the QnA
const qnaResults = await qnaMaker.getAnswers(turnContext);
// If an answer was received from QnA Maker, send the answer back to the user.
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 turnContext.sendActivity(reply);
} else {
let errorCardFB = await fbCards.getErrorCardFB(fbPSID);
await turnContext.sendActivity(errorCardFB);
}
};
module.exports = {
processFacebookPayload: processFacebookPayload,
onFacebookPostback: onFacebookPostback,
onFacebookOptin: onFacebookOptin,
onFacebookQuickReply: onFacebookQuickReply
};
@seanbetts, I noticed an issue that is tied to the parameters you are passing in to your bot and how they are aligned to the parameters in the bot's constructor.
In your index.js
file, you have opted not to create a dialog and are passing in the transcriptStore. Normally, the file would look something like this:
logger = console;
// Create the main dialog.
const dialog = new MainDialog('MainDialog', userState, conversationState);
const bot = new DialogBot(conversationState, userState, dialog, logger);
However, with the removal of dialog
and the addition of transcriptStore
, the order has changed. Additionally, the transcriptStore
is part of the middleware you already have instantiated earlier in your code. Passing it here is unnecessary and should be removed.
logger = console;
// Create the main dialog.
// const dialog = new UserProfileDialog(userState, logger);
const bot = new DialogBot(conversationState, userState, logger, transcriptStore);
In your dialog.js
file, your constructor remains unchanged. Hence, you are passing the console logger
in for the dialog
and the transcriptStore
in as your logger
.
class DialogBot extends ActivityHandler {
constructor(conversationState, userState, dialog, logger) {
[...]
}
}
Try removing transcriptStore
from inside the index.js const bot
assignment and the dialog
property from the constructor.
That being said, in your index.js
file, you assign const {DialogBot} = require('./bots/dialogBot')
but the file you list above is dialog.js
which, unless I'm missing it, isn't called. I'm not fully understanding how your bot is structured. From what I can see, your bot should fail altogether.
Hi @stevkan thanks for getting back to me. Apologies, my dialog file is in fact called dialogBot.js
, I'll update my post. Thank you as well for the suggestions on the dialog
property, that was left over from some earlier testing and I've now removed it from both the index.js
and dialogBot.js
files. Despite the issues you've pointed out, my chat bot has actually been running. I've just been checking the logs and I do get the following error when the chatbot first starts: OPTIONS /api/messages 405 Method Not Allowed
. Could this have something to do with the transcript duplication? I have also noticed another error in the console when using the Facebook Messenger channel 500 ERROR - Error: BotFrameworkAdapter.sendActivity(): missing conversation id.
. This is probably a more likely cause of the transcripts duplication so I'm going to do some more investigating.
The 500 ERROR
indicates an error in the bot's code. Have you tried debugging, running the bot locally but through a tunnel (ngrok or service bus)? Then you can test on different channels stepping through the code looking for the error(s) to occur.
Thanks @stevkan - this is the same issue that I raised here. I thought that was solved when I checked my Facebook credentials but I've double checked them again and all is in order. From doing some further investigation I am receiving this error whenever I send a postback or quick reply back to Facebook Messenger. Could it be something to do with the way BotFramework and the Facebook Messenger channel are integrated? I've checked my Facebook channel configuration and it is showing issues with the same timestamp as when I have been sending messages. The error message is There was an error sending this message to your bot: HTTP status code InternalServerError
I've also checked on Facebook's side and they are logging errors with the same timestamp as well that states The requested URL returned error: 503 Service Unavailable
Can you list for me the web hooks are you subscribed to?
Sure, I've got them all selected:
I'm guessing I don't need all of these enabled and that may be causing the problem?
Thanks for taking the time to look at this
Hi @stevkan I've turned off all the web hooks except the basic ones - messages, messaging_postbacks, message_deliveries and messaging_optins and this seems to have fixed the problem
Sorry, I've just double checked and I'm still getting duplicated logs from the Facebook channel. Changing the web hooks has solved errors but not the log duplication
How about the type of events being logged? The logs you include show trace events. Is it specific to that type or does it matter?
It’s just the trace events that are duplicated
Ok, two things:
https://facebook.botframework.com
) as a trusted service url? This can be done by entering code such as the following in your "dialogBot.js" file (or similar). This can help mitigate some errors which you had listed, previously. I understand some of those disappeared with the simplification of webhooks, but I would like to eliminate this as a possible underlying cause.this.onTurn( async ( context, next ) => {
const serviceUrl = "https://facebook.botframework.com";
if ( context.activity.channelId === 'facebook' ) {
MicrosoftAppCredentials.trustServiceUrl(serviceUrl)
}
await next();
})
@seanbetts, do you still require assistance? The thread must be active in order to keep it open.
Hi @stevkan and @CoHealer, yes - I'm still having issues. I have just tried to implement @stevkan suggestion of adding the Facebook service url as a trusted service but I don't know how to define MicrosoftAppCredentials and get the error MicrosoftAppCredentials is not defined
.
@stevkan - to your other question, I am getting individual log files for each event and it's only the trace events which are duplicated.
@seanbetts, add this to the top of you dialogBot.js
file, then add the previously mentioned code.
const {
MicrosoftAppCredentials
} = require( 'botframework-connector' );
Thanks @stevkan - I've just been testing this and I'm still getting duplicate trace logs.
@seanbetts, if you are willing to meet over Skype (or some other), can you send me an email to the address listed in my profile with some times that might work for meeting up? I'd like to see what is happening, in real time, if possible.
[@seanbetts and I are set to meet via Teams to review issue on Friday, Sept. 27 at 15:30 GMT]
@stevkan and @seanbetts did you meet and was there any conclusion?
@seanbetts and I did meet over Teams with no resolution. His bot's settings for the logger appear to be in order. The logger, with respect to Facebook, are creating what appear to be duplicate log files when trace activities populate the logs.
The customer sent the duplicate logs to review. Noticed that the entirety of each file is duplicated with the exception of the final timestamp. I believe this is just a function of the log being created, but need to verify, as the activity's timestamp is the same.
Retested using the provided code and still was unable to repro the issue with each trace saving as an individual, unique file. I did notice that the trace's timestamps, in my tests, had times prior to the QnA result timestamp whereas the customer's traces had timestamps posted after the QnA result timestamp. Going to reach out to devs for guidance.
Is it possible that you're getting two facebook messages per turn? Maybe facebook is calling you twice for each message...
Hi @Stevenic I don’t think that’s possible. If I was then I’d be getting two responses in the chatbot.
Looking back over your code you're actually calling qnaMaker.getAnswers()
multiple times. You first call it here:
this.onMessage(async (context, next) => {
await sendTyping(context);
console.log('Processing a Message Activity');
>>> 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)) {
And then again within your fbBot:
if (postback.payload === 'WHAT_IS_NEURODIVERSITY') {
// qnaMaker.getAnswers doesn't accept string input, so we need to adjust our turnContext
// to match what it expects, which is a string in Activity.Text
turnContext.activity.text = postback.payload;
// call the QnA
>>> const qnaResults = await qnaMaker.getAnswers(turnContext);
Each call to qnaMaker is going to generate a new trace. Given that you call qnaMaker at the start of your onMessage
handler you should pass those results through to your fbBot instead of having it call qnaMaker a second time.
Closing as resolved. If the issue persists, please feel free to reopen.
Did anyone solve this issue? Please help me to resolve this
[onTurnError]: Error: BotFrameworkAdapter.sendActivity(): missing serviceUrl. (node:14604) UnhandledPromiseRejectionWarning: Error: missing activity.channelId at ConversationState.getStorageKey (D:\home\site\wwwroot\node_modules\botbuilder-core\lib\conversationState.js:42:19) at ConversationState [as storageKey] (D:\home\site\wwwroot\node_modules\botbuilder-core\lib\conversationState.js:28:30) at ConversationState.delete (D:\home\site\wwwroot\node_modules\botbuilder-core\lib\botState.js:141:37) at BotFrameworkAdapter.adapter.onTurnError [as turnError] (D:\home\site\wwwroot\index.js:150:35) at middleware.run.then (D:\home\site\wwwroot\node_modules\botbuilder-core\lib\botAdapter.js:87:26) at process._tickCallback (internal/process/next_tick.js:68:7) (node:14604) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
Versions
BotBuilder SDK v4.4.0 node.js v10.16.0
Describe the bug
I have implemented transcripLogger in my chatbot and I have noticed that I am getting duplicate trace logs for the Facebook channel. This isn't happening for the WebChat channel.
The duplicate Facebook channel logs are identical in every way except the filename that they're saved as. They even have the same timestamp.
To Reproduce
Expected behavior
I expect individual log items for each Facebook channel message
Example duplicate logs
Log 1 - filename: 2005698806194679-550063992071761/8d724e2af871980-undefined.json
Log 2 - filename: 2005698806194679-550063992071761/8d724e2afb1fa00-undefined.json
[bug]