How do I trigger LUIS in all dialogs retaining the previous context? - node.js

I am developing a bot with Microsoft Bot Framework, LUIS and node.js .This bot is based on Microsoft Teams and I am using TeamsBotSsoPrompt and Microsoft-Graph for authentication of the user.
The authentication step in the AUTH_DIALOG is as follows:
async showUserInfo(stepContext) {
const tokenResponse = stepContext.result;
if (tokenResponse) {
try {
// Call Microsoft Graph on behalf of user
const oboCredential = new OnBehalfOfUserCredential(tokenResponse.ssoToken);
//console.log("oboCredential> " + JSON.stringify(oboCredential));
const graphClient = createMicrosoftGraphClient(oboCredential, this.requiredScopes);
const me = await graphClient.api("/me").get();
const lanID = await graphClient.api("/me/onPremisesSamAccountName").get();
if (me) {
var authHeaders = {
"user": me.userPrincipalName,
"token": tokenResponse.token,
header1: null,
header2: null,
header3: null,
}
userName = me.givenName;
authHeaders['name'] = userName;
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity(`Hello! <b>` + userName + `</b>`);
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Let's get started.How can I help you?");
return await stepContext.beginDialog(FLOW_DIALOG, authHeaders);
} else {
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Getting profile from Microsoft Graph failed! ");
return await stepContext.endDialog();
}
}
catch (error) {
// error logged here
}
}
else {
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity("Login was not successful please try again.");
return await stepContext.endDialog();
}
}
The FLOW_DIALOG is where I have LUIS enabled.From the FLOW_DIALOG, I redirect the bot to other dialogs based on the Intent.But when I am in that other dialog and if I enter something in the bot, the current dialog ends and returns the flow to the FLOW_DIALOG. I want all the dialogs to trigger LUIS and move the flow to the correct Intent Dialog.
The FLOW_DIALOG is as follows:
async introStep(stepContext) {
const ActionCard = utility.createSuggestedActions(suggestionList, suggestionTitle);
await stepContext.context.sendActivity({ type: ActivityTypes.Typing });
await stepContext.context.sendActivity({ attachments: [ActionCard] });
return { status: DialogTurnStatus.waiting };
}
async actStep(stepContext) {
const luisRecognizer = new LuisRecogniser(luisConfig, stepContext.dialogs.telemetryClient);
let authHeaders = stepContext.options;
if (!luisRecognizer.isConfigured) {
return await stepContext.context.sendActivity("Something went wrong. Please Contact Your Administrator");
}
const luisResult = await luisRecognizer.executeLuisQuery(stepContext.result ? stepContext.result : stepContext.context);
topIntent = LuisRecognizer.topIntent(luisResult);
switch (topIntent) {
case "Intent1": {
authHeaders['header1'] = luisResult.entities;
return await stepContext.beginDialog(DIALOG_A, authHeaders);
}
case "Intent2": {
authHeaders['header2'] = luisResult.entities;
return await stepContext.beginDialog(DIALOG_B, authHeaders);
}
// ... and other intents in similar way
}
}
Now, I did try adding the LUIS to the onMessage call but I couldn't access the context.options to set or reset the authHeaders that I have. I know the context there is DialogContext and the one we get access to is TurnContext.
So is it possible to alter the headers values and pass it along the dialog calls and also trigger LUIS for every message the user enters from whichever Dialog the user currently has access to.

Having LUIS integrated within Waterfall dialogs seems not supported. It worked fine in case of TextPrompt but I find no way to do that when we have 'ChoicePrompt' or 'ConfirmPrompt'.
We can have LUIS integrated to decide which dialog to start and for the same sample is also available - Core bot.
You can also refer this link to have idea on how to use dialogs in bot - https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-concept-waterfall-dialogs?view=azure-bot-service-4.0#using-dialogs

Related

Firebase function TypeError .once is not a function and onCreate not working

I was trying to deploy a function to Firebase to send notifications to all admin accounts when a new user signs up to the app, this is my current code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.newDoctorNotification = functions.database.ref("/doctors/{pushId}")
.onCreate((snapshot, context) => {
const newDoctorID = context.params.pushId;
const notificationContent = {
notification: {
title: "New Doctor",
body: "A new doctor just signed up! uid: " + newDoctorID,
icon: "default",
sound: "default",
},
};
const adminTokensRef = functions.database.ref("device_tokens/admin");
const tokens = [];
adminTokensRef.once("value", (querySnapshot) => {
querySnapshot.forEach((adminToken) => {
tokens.push(adminToken.val());
});
});
if (tokens.length > 0) {
return admin.messaging().sendToDevice(tokens, notificationContent)
.then(function(result) {
console.log("Notification sent");
return null;
})
.catch(function(error) {
console.log("Notification failed ", error);
return null;
});
}
});
I have tried many variations such as the get() function and on(), but all give me the same error, I was trying to check the docs on this but they only talked about database triggers so I'm not sure if normal retrieval can work or not.
EDIT:
I updated my code to reach the database node through the snapshot given in the onCreate event, and now it works, although I am facing another problem now, if I push a new doctor under the node "doctors" it doesn't call the function.. but if I hit "test" in the Google Cloud console and it calls the function I get "null" in my snapshot.val() and "undefined" in the newDoctorID above, whereas the snapshot.key gives "doctors", why is it not calling the onCreate function?

Add authentication and Qnamaker to bot

I am trying to build a bot using bot framework sdk.
Objective:-
To authenticate user.
afte authentication communicate with Qnamaker knowledge base.
signout user if he types logout
I am using bot authentication template from https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/18.bot-authentication. I have tried to add qnamaker service in the waterflowdialog.
I am facing two issues:-
With every Qnamaker reply I am getting message "you are now logged in".
Bot encountered an error.
Source Code
mainDialog.js
const {
ConfirmPrompt,
DialogSet,
DialogTurnStatus,
OAuthPrompt,
WaterfallDialog,
} = require("botbuilder-dialogs");
const { LogoutDialog } = require("./logoutDialog");
const { QnAMakerDialog } = require("botbuilder-ai");
const CONFIRM_PROMPT = "ConfirmPrompt";
const MAIN_DIALOG = "MainDialog";
const MAIN_WATERFALL_DIALOG = "MainWaterfallDialog";
const OAUTH_PROMPT = "OAuthPrompt";
const QNAMAKER_BASE_DIALOG = "qnamaker-base-dialog";
const createQnAMakerDialog = (
knowledgeBaseId,
endpointKey,
endpointHostName,
defaultAnswer
) => {
let noAnswerActivity;
if (typeof defaultAnswer === "string") {
noAnswerActivity = MessageFactory.text(defaultAnswer);
}
const qnaMakerDialog = new QnAMakerDialog(
knowledgeBaseId,
endpointKey,
endpointHostName,
noAnswerActivity
);
qnaMakerDialog.id = QNAMAKER_BASE_DIALOG;
return qnaMakerDialog;
};
class MainDialog extends LogoutDialog {
constructor(knowledgeBaseId, endpointKey, endpointHostName, defaultAnswer) {
super(MAIN_DIALOG, process.env.connectionName);
this.addDialog(
new OAuthPrompt(OAUTH_PROMPT, {
connectionName: process.env.connectionName,
text: "Please Sign In",
title: "Sign In",
timeout: 300000,
})
);
this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT));
this.addDialog(
new WaterfallDialog(MAIN_WATERFALL_DIALOG, [
this.promptStep.bind(this),
this.loginStep.bind(this),
this.qnaMaker.bind(this),
])
);
this.addDialog(
createQnAMakerDialog(
knowledgeBaseId,
endpointKey,
endpointHostName,
defaultAnswer
)
);
this.initialDialogId = MAIN_WATERFALL_DIALOG;
}
/**
* The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
* If no dialog is active, it will start the default dialog.
* #param {*} dialogContext
*/
async run(context, accessor) {
const dialogSet = new DialogSet(accessor);
dialogSet.add(this);
const dialogContext = await dialogSet.createContext(context);
const results = await dialogContext.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dialogContext.beginDialog(this.id);
}
}
async qnaMaker(stepContext) {
await stepContext.beginDialog(QNAMAKER_BASE_DIALOG);
}
async promptStep(stepContext) {
return await stepContext.beginDialog(OAUTH_PROMPT);
}
async loginStep(stepContext) {
// Get the token from the previous step. Note that we could also have gotten the
// token directly from the prompt itself. There is an example of this in the next method.
const tokenResponse = stepContext.result;
if (tokenResponse) {
await stepContext.context.sendActivity("You are now logged in");
return await stepContext.next();
}
await stepContext.context.sendActivity(
"Login was not successful please try again."
);
return await stepContext.endDialog();
}
}
module.exports.MainDialog = MainDialog;
Bot ScreenShot
github link : https://github.com/chandelsumeet/authBot
async loginStep(stepContext) {
// Get the token from the previous step. Note that we could also have gotten the
// token directly from the prompt itself. There is an example of this in the next method.
const tokenResponse = stepContext.result;
if (tokenResponse) {
await stepContext.context.sendActivity('You are now logged in.');
return await stepContext.prompt(CONFIRM_PROMPT, 'Would you like to view your token?');
}
await stepContext.context.sendActivity('Login was not successful please try again.');
return await stepContext.endDialog();
}
Replace the loginStep() with above code block and check it.

Botframework how to authenticate first and than use the token to make graph api calls

Auth Dialog:
import { ChoicePrompt, DialogSet, DialogTurnStatus, OAuthPrompt, TextPrompt, WaterfallDialog, ComponentDialog } from 'botbuilder-dialogs';
import GraphClient from '../graph-client';
const MAIN_WATERFALL_DIALOG = 'mainWaterfallDialog';
const OAUTH_PROMPT = 'oAuthPrompt';
const CHOICE_PROMPT = 'choicePrompt';
const TEXT_PROMPT = 'textPrompt';
import moment = require('moment');
class AuthDialog extends ComponentDialog {
constructor() {
super('AuthDialog');
this.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new OAuthPrompt(OAUTH_PROMPT, {
connectionName: process.env.ConnectionName,
text: 'Please login',
title: 'Login',
timeout: 300000
}))
.addDialog(new TextPrompt(TEXT_PROMPT))
.addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [
this.promptStep.bind(this),
this.processStep.bind(this)
]));
this.initialDialogId = MAIN_WATERFALL_DIALOG;
}
/**
* 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
*/
public 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);
}
}
public async promptStep(step) {
return step.beginDialog(OAUTH_PROMPT);
}
public async processStep(step) {
if (step.result) {
// We do not need to store the token in the bot. When we need the token we can
// send another prompt. If the token is valid the user will not need to log back in.
// The token will be available in the Result property of the task.
const tokenResponse = step.result;
// If we have the token use the user is authenticated so we may use it to make API calls.
if (tokenResponse && tokenResponse.token) {
await step.context.sendActivity(`Logged in.`);
} else {
await step.context.sendActivity('something wrong happened.');
}
} else {
await step.context.sendActivity('We couldn\'t log you in. Please try again later.');
}
return await step.endDialog();
}
}
export default AuthDialog;
I have a main dailog which is connected to luis and based on the intent recognized it executes corrosponding code:
for ex i have this in some cases:
case 'CalendarEvents':
return stepContext.beginDialog('AuthDialog');
const calendar = await new GraphClient('token').events();
let eventsBuilder: string = '';
// tslint:disable-next-line: prefer-for-of
for (let index = 0; index < calendar.length; index++) {
const element = calendar[index];
eventsBuilder += '\r\n' + moment(element.start.dateTime).format('dddd, MMMM Do YYYY, h:mm:ss a') + ' - ' + element.subject;
}
await step.context.sendActivity(`${eventsBuilder}`);
So if the intent is CalendarEvents then authenticate and than make some graph api call.
The problem I currently have is that the call to graph api is made before the auth is finished, I would like so first user authenticate and than receives some token and use that token for fetching graph api calls!
any idea how to achieve the above?
Please see the Graph Auth Sample. In particular,
It gets the token in MainDialog:
return step.beginDialog(OAUTH_PROMPT);
[...]
if (step.result) {
const tokenResponse = step.result;
if (tokenResponse && tokenResponse.token) {
const parts = (step.values.command || '').toLowerCase().split(' ');
const command = parts[0];
switch (command) {
case 'me':
await OAuthHelpers.listMe(step.context, tokenResponse);
break;
case 'send':
await OAuthHelpers.sendMail(step.context, tokenResponse, parts[1]);
break;
case 'recent':
await OAuthHelpers.listRecentMail(step.context, tokenResponse);
break;
default:
await step.context.sendActivity(`Your token is ${ tokenResponse.token }`);
}
}
}
[...]
Then, OAuthHelpers uses the token:
static async sendMail(context, tokenResponse, emailAddress) {
[...]
const client = new SimpleGraphClient(tokenResponse.token);
const me = await client.getMe();
await client.sendMail(
emailAddress,
'Message from a bot!',
`Hi there! I had this message sent from a bot. - Your friend, ${ me.displayName }`
);
await context.sendActivity(`I sent a message to ${ emailAddress } from your account.`);
}
This is how the sample works. For you, since you only want to do auth in the Auth dialog, you need to get the token from the user using the Auth dialog, then save it to their UserState, similar to this sample. You can then retrieve their UserState in any dialog and use the token if they have it.
Note
You can either use the Graph API through regular HTTP REST API requests, or use the Graph SDK like the sample does.

Send user notification every hour with Bot Framework NodeJS

I am developing a bot that sends users a message every hour on skype. It makes an API call and then sends a message containing the parsed message. I started with the welcomebot sample. I then used the switch statement to fire a setInterval()to send the message activity when the user sends a certain message but the message keeps sending even after I call clearinterval() I set to a different string. How can I make clearInterval() work? Or am I going about it the wrong way?
Index.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required packages
const path = require('path');
const restify = require('restify');
const axios = require('axios');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter, UserState, ConversationState, MemoryStorage } = require('botbuilder');
const { WelcomeBot } = require('./bots/welcomeBot');
// Read botFilePath and botFileSecret from .env file
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create bot adapter.
// See https://aka.ms/about-bot-adapter to learn more about bot adapter.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppID,
appPassword: process.env.MicrosoftAppPassword
});
// Catch-all for errors.
adapter.onTurnError = async (context, 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.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};
// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
// A bot requires a state store to persist the dialog and user state between messages.
// For local development, in-memory storage is used.
// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot
// is restarted, anything stored in memory will be gone.
const memoryStorage = new MemoryStorage();
const userState = new UserState(memoryStorage);
const conversationState = new ConversationState(memoryStorage);
// Create the main dialog.
const conversationReferences = {};
const bot = new WelcomeBot(userState, conversationReferences, conversationState);
// Create HTTP server
const 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');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
// Listen for incoming activities and route them to your bot main dialog.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// route to main dialog.
await bot.run(context);
});
});
const getBalance = async () => {
try {
return await axios.get('https://balance.com/getbalance', { //this returns a json object
headers: {
Authorization: xxxxx
}
});
} catch (error) {
console.error(error);
}
};
const retrieveBalance = async () => {
const balance = await getBalance();
if (balance.data.message) {
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async turnContext => {
// If you encounter permission-related errors when sending this message, see
// https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity(balance.data.message);
});
}
}
};
module.exports.retrieveBalance = retrieveBalance;
Bot.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required Bot Framework classes.
const { ActionTypes, ActivityHandler, CardFactory, TurnContext } = require('botbuilder');
const balance = require('../index.js');
// Welcomed User property name
const CONVERSATION_DATA_PROPERTY = 'conversationData';
class WelcomeBot extends ActivityHandler {
/**
*
* #param {UserState} //state to persist boolean flag to indicate
* if the bot had already welcomed the user
*/
constructor(userState, conversationReferences, conversationState) {
super();
// Creates a new user property accessor.
// See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors.
this.userState = userState;
this.conversationReferences = conversationReferences;
this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// The state management objects for the conversation and user state.
this.conversationState = conversationState;
this.onConversationUpdate(async (context, next) => {
this.addConversationReference(context.activity);
await next();
});
this.onMessage(async (context, next) => {
this.addConversationReference(context.activity);
// Add message details to the conversation data.
// Display state data.
// Read UserState. If the 'DidBotWelcomedUser' does not exist (first time ever for a user)
// set the default to false.
// const didBotWelcomedUser = await this.welcomedUserProperty.get(context, false);
// Your bot should proactively send a welcome message to a personal chat the first time
// (and only the first time) a user initiates a personal chat with your bot.
// if (didBotWelcomedUser === false) {
// // The channel should send the user name in the 'From' object
// await context.sendActivity( 'This bot will send you your paystack balance every 5 minutes. To agree reply \'ok\'');
// // Set the flag indicating the bot handled the user's first message.
// await this.welcomedUserProperty.set(context, true);
// }
// This example uses an exact match on user's input utterance.
// Consider using LUIS or QnA for Natural Language Processing.
var text = context.activity.text.toLowerCase();
var myint;
switch (text) {
case 'ok':
myint = setInterval(async () => {
await balance.retrieveBalance();
}, 1000);
// eslint-disable-next-line template-curly-spacing
break;
case 'balance':
await balance.retrieveBalance();
break;
case 'no':
clearInterval(myint);
break;
case 'hello':
case 'hi':
await context.sendActivity(`You said "${ context.activity.text }"`);
break;
case 'info':
case 'help':
await this.sendIntroCard(context);
break;
default:
await context.sendActivity('This is a simple notification bot. ');
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
// Sends welcome messages to conversation members when they join the conversation.
// Messages are only sent to conversation members who aren't the bot.
this.onMembersAdded(async (context, next) => {
// Iterate over all new members added to the conversation
for (const idx in context.activity.membersAdded) {
// Greet anyone that was not the target (recipient) of this message.
// Since the bot is the recipient for events from the channel,
// context.activity.membersAdded === context.activity.recipient.Id indicates the
// bot was added to the conversation, and the opposite indicates this is a user.
if (context.activity.membersAdded[idx].id !== context.activity.recipient.id) {
await context.sendActivity('Welcome to the Nag Bot. If you want to get nagged with your balance type \'ok\', if you want your balance alone type \'balance. for info type \'info\'');
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);
// Save state changes
await this.userState.saveChanges(context);
await this.conversationState.saveChanges(context, false);
}
addConversationReference(activity) {
const conversationReference = TurnContext.getConversationReference(activity);
this.conversationReferences[conversationReference.conversation.id] = conversationReference;
}
async sendIntroCard(context) {
const card = CardFactory.heroCard(
'Welcome to Nag Bot balance checker!',
'Welcome to Paystack Nagbot.',
['https://aka.ms/bf-welcome-card-image'],
[
{
type: ActionTypes.OpenUrl,
title: 'Open your dashboard',
value: 'https://dashboard.paystack.com'
},
{
type: ActionTypes.OpenUrl,
title: 'Ask a question on twitter',
value: 'https://twitter.com/paystack'
},
{
type: ActionTypes.OpenUrl,
title: 'View docs',
value: 'https://developers.paystack.co/reference'
}
]
);
await context.sendActivity({ attachments: [card] });
}
}
module.exports.WelcomeBot = WelcomeBot;

Bot Framework v4 Node.js Location

I'm in the process of designing a chat bot and trying to find some Node.js sample code and/or documentation on how to implement the Azure Maps service as part of Bot Framework V4. There are many examples of how this is accomplished in V3, but there seems to be no examples of a V4 solution for Node.js. I'm looking to create a step in my botbuilder-dialog flow that would launch a simple "where do we ship it too" location dialog that would guide the user through the dialog and store the address results as part of that users profile. Any help or advice on this would be appreciated.
Yes, this is doable. I created a class (probably overkill, but oh well) in which I make my API call, with my supplied parameters, to get the map. I decided to use Azure Maps (vs Bing Maps) only because I was curious in how it differed. There isn't any reason you couldn't do this with Bing Maps, as well.
In the bot, I am using a component dialog because of how I have the rest of my bot designed. When the dialog ends, it will fall off the stack and return to the parent dialog.
In my scenario, the bot presents the user with a couple choices. "Send me a map" generates a map and sends it in an activity to the client/user. Anything else sends the user onward ending the dialog.
You will need to decide how you are getting the user's location. I developed this with Web Chat in mind, so I am getting the geolocation from the browser and returning it to the bot to be used when getMap() is called.
const { ActivityTypes, InputHints } = require('botbuilder');
const fetch = require('node-fetch');
class MapHelper {
async getMap(context, latitude, longitude) {
var requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow'
};
const result = await fetch(`https://atlas.microsoft.com/map/static/png?subscription-key=${ process.env.AZURE_MAPS_KEY }&api-version=1.0&layer=basic&zoom=13&center=${ longitude },${ latitude }&language=en-US&pins=default|al.67|la12 3|lc000000||'You!'${ longitude } ${ latitude }&format=png`, requestOptions)
.then(response => response.arrayBuffer())
.then(async result => {
const bufferedData = Buffer.from(result, 'binary');
const base64 = bufferedData.toString('base64');
const reply = { type: ActivityTypes.Message };
const attachment = {
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64 }`
};
reply.attachments = [attachment];
await context.sendActivity(reply, null, InputHints.IgnoringInput);
})
.catch(error => {
if (error) throw new Error(error);
});
return result;
};
};
module.exports.MapHelper = MapHelper;
const { ChoicePrompt, ChoiceFactory, ComponentDialog, ListStyle, WaterfallDialog } = require('botbuilder-dialogs');
const { MapHelper } = require('./mapHelper');
const CONFIRM_LOCALE_DIALOG = 'confirmLocaleDialog';
const CHOICE_PROMPT = 'confirmPrompt';
class ConfirmLocaleDialog extends ComponentDialog {
constructor() {
super(CONFIRM_LOCALE_DIALOG);
this.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(CONFIRM_LOCALE_DIALOG, [
this.askLocationStep.bind(this),
this.getMapStep.bind(this)
]));
this.initialDialogId = CONFIRM_LOCALE_DIALOG;
}
async askLocationStep(stepContext) {
const choices = ['Send me a map', "I'll have none of this nonsense!"];
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: 'Good sir, may I pinpoint you on a map?',
choices: ChoiceFactory.toChoices(choices),
style: ListStyle.suggestedAction
});
}
async getMapStep(stepContext) {
const { context, context: { activity } } = stepContext;
const text = activity.text.toLowerCase();
if (text === 'send me a map') {
const { latitude, longitude } = activity.channelData;
const mapHelper = new MapHelper();
await mapHelper.getMap(context, latitude, longitude);
const message = 'Thanks for sharing!';
await stepContext.context.sendActivity(message);
return await stepContext.endDialog();
} else {
await stepContext.context.sendActivity('No map for you!');
return await stepContext.endDialog();
}
}
}
module.exports.ConfirmLocaleDialog = ConfirmLocaleDialog;
module.exports.CONFIRM_LOCALE_DIALOG = CONFIRM_LOCALE_DIALOG;
Hope of help!
---- EDIT ----
Per request, location data can be obtained from the browser using the below method. It is, of course, dependent on the user granting access to location data.
navigator.geolocation.getCurrentPosition( async (position) => {
const { latitude, longitude } = position.coords;
// Do something with the data;
console.log(latitude, longitude)
})

Resources