How to cancel skill's Dialog from Virtual Assistant while using MS Bot Framework V4 - botframework

We are using bot-framework version 4.x
"botbuilder": "4.11.0",
"botbuilder-ai": "4.11.0",
"botbuilder-applicationinsights": "4.11.0",
"botbuilder-azure": "4.11.0",
"botbuilder-dialogs": "4.11.0",
"botbuilder-lg": "4.11.0",
"botbuilder-testing": "4.11.0",
"botframework-config": "4.11.0",
"botframework-connector": "4.11.0"
Case
We need to start a Dialog in Virtual Assistant and cancel any ongoing Skill dialog (If any)
Example
LiveChat - part of Virtual Assistant
CreateIncidentDialog - part of skill
Suppose we are in the middle of CreateIncidentDialog and the user wants to start LiveChat. We are supposed to start LiveChat and cancel current CreateIncidentDialog.
Sample Code
MainDialog.ts in Virtual Assistant
If current Dialog is a part of skill, we are starting a dialog with activity.type as EVENT for skill.
switch (gIntent) {
case 'Escalate': {
// Checking if current Dialog is a part of Skill
if (isSkill) {
const activity: Activity = innerDc.context.activity;
if ((activity.value === null || activity.value === undefined)) {
activity.value = { userState: userProfile };
};
activity.type = ActivityTypes.Event;
activity.name = "liveChat";
const skillDialogArgs: BeginSkillDialogOptions = {
activity: activity as Activity
};
await innerDc.beginDialog(EvaConfig.LUIS_DEFAULT_INTENT, skillDialogArgs);
} else {
await innerDc.cancelAllDialogs();
}
// code for starting LiveChat goes here
// .....
// .....
// .....
}
MainDialog.ts in Skill
If activity.type is EVENT and name is 'liveChat', cancel dialogs.
protected async onContinueDialog(innerDc: DialogContext): Promise<DialogTurnResult> {
try {
let activity = innerDc.context.activity;
if (innerDc.context.activity.type === ActivityTypes.Message) {
// .....
// .....
// .....
} else if (activity.type === ActivityTypes.Event) {
const ev = activity;
if (ev.name !== undefined && ev.name.trim().length > 0) {
switch (ev.name) {
case 'liveChat': {
await innerDc.cancelAllDialogs(true);
return await innerDc.endDialog();
}
}
}
}
return await super.onContinueDialog(innerDc);
} catch (e) {
console.log(`Error onContinueDialog ---> ${e} `);
return await this.hanldeException(innerDc);
}
}
Issue
The above code is not able to cancel the skill's dialog correctly. If we try to start a new dialog after the cancellation of skill dialog, it again sends an activity with activity.type as EVENT to routeStep method of MainDilaog.ts in Skill. Please find the sample code below for the same.
routeStep method of MainDialog.ts in Skill
protected async routeStep(stepContext: WaterfallStepContext): Promise<DialogTurnResult> {
console.log('skill routeStep');
if (activity.type === ActivityTypes.Message && activity.text !== undefined && activity.text.trim().length > 0) {
// .....
// .....
// .....
} else if (activity.type === ActivityTypes.Event) {
const ev = activity;
if (ev.name !== undefined && ev.name.trim().length > 0) {
switch (ev.name) {
case 'SampleAction': {
// .....
// .....
// .....
}
}
} else {
// If we try to start a new dialog, control comes here
await stepContext.context.sendActivity({
type: ActivityTypes.Trace,
text: 'An event with no name was received but not processed.'
});
}
}
} else {
await this.responder.replyWith(stepContext.context, MainResponses.responseIds.dontHavePermissionServiceNow);
await this.complete(stepContext);
}
return await stepContext.next();
}
Step to Reproduce
Step 1. Start a Dialog in Skill
Step 2. End the Dialog in the Skill and start a Dialog in VA
Step 3. Start a new Dialog
Thanks.
Edward

Related

Xamarin - Switch statement for Follow and unFollow

I'm trying to make a follow and unfollow techniques to user profile
I made this code but the main problem is that the user followed successfully but if you tap the button again the toast will show that unfollowed and the button image will change (like you will follow again) but if you refresh the following image show again it's like nothing happen
switch (BtnFollow?.Tag?.ToString())
{
case "Add": //Sent follow
BtnFollow.SetColor(Color.ParseColor(AppSettings.MainColor));
BtnFollow.SetImageResource(Resource.Drawable.ic_tick);
BtnFollow.Tag = "friends";
DataUser.IsFollowing = true;
Toast.MakeText(Context, Context.GetText(Resource.String.Lbl_Sent_successfully_followed),
ToastLength.Short)?.Show();
PollyController.RunRetryPolicyFunction(new List<Func<Task>> { () => RequestsAsync.User.FollowUnFollowUserAsync(UserId, true) });
break;
case "friends": //Sent un follow
BtnFollow.SetColor(Color.ParseColor("#444444"));
BtnFollow.SetImageResource(Resource.Drawable.ic_add);
BtnFollow.Tag = "Add";
DataUser.IsFollowing = false;
Toast.MakeText(Context, Context.GetText(Resource.String.Lbl_Sent_successfully_Unfollowed),
ToastLength.Short)?.Show();
PollyController.RunRetryPolicyFunction(new List<Func<Task>> { () => RequestsAsync.User.FollowUnFollowUserAsync(UserId, false) });
break;
}
var dataUser = GlobalContext?.MainFragment?.ArtistsAdapter?.ArtistsList?.FirstOrDefault(a => a.Id == DataUser.Id);
if (dataUser != null)
{
dataUser.IsFollowing = DataUser.IsFollowing;
GlobalContext.MainFragment.ArtistsAdapter.NotifyDataSetChanged();
}
}
catch (Exception exception)
{
Methods.DisplayReportResultTrack(exception);
}
}
and this code to check the following status
if (DataUser.IsFollowing != null && DataUser.IsFollowing.Value) // My Friend
{
BtnFollow.SetColor(Color.ParseColor(AppSettings.MainColor));
BtnFollow.SetImageResource(Resource.Drawable.ic_tick);
BtnFollow.Tag = "friends";
}
else //Not Friend
{
BtnFollow.SetColor(Color.ParseColor("#444444"));
BtnFollow.SetImageResource(Resource.Drawable.ic_add);
BtnFollow.Tag = "Add";
}
}

Geolocation functionality of Xamarin Essentials wrong location results

I am using the Geolocation functionality of Xamarin Essentials in my app as a foreground service running all the time and I have noticed on several devices that I sometimes get locations which are really far away from where they should actually be, looking like this (the marked location is far away from the real location):
public async Task Run(CancellationToken token)
{
await Task.Run(async () => {
while (!stopping)
{
token.ThrowIfCancellationRequested();
try
{
await Task.Delay(2000);
var request = new GeolocationRequest(GeolocationAccuracy.Best);
var location = await Geolocation.GetLocationAsync(request);
if (location != null)
{
var message = new LocationMessage
{
Latitude = location.Latitude,
Longitude = location.Longitude
};
Device.BeginInvokeOnMainThread(() =>
{
MessagingCenter.Send<LocationMessage>(message, "Location");
});
}
}
catch (Exception ex)
{
Device.BeginInvokeOnMainThread(() =>
{
var errormessage = new LocationErrorMessage();
MessagingCenter.Send<LocationErrorMessage>(errormessage, "LocationError");
});
}
}
return;
}, token);
}
result

Virtual assistant created using Typscript- running in Bot Framework Emulator is not responding

I am trying to develop virtual assistant using typescript . i have followed this below document
https://microsoft.github.io/botframework-solutions/tutorials/typescript/create-assistant/1_intro/
When i run npm start and test it in Botframework emulator , the bot is not responding any message.
But the Bot is opening with new user greeting adaptive card message
I have tried to edit the adaptive greeting card following this document page
https://microsoft.github.io/botframework-solutions/tutorials/typescript/customize-assistant/2_edit_your_greeting/
but eventhough the bot is not replying any message
`[11:53:22]Emulator listening on http://localhost:49963`
[11:53:22]ngrok listening on https://b2915c2d.ngrok.io
[11:53:22]ngrok traffic inspector:http://127.0.0.1:4040
[11:53:22]Will bypass ngrok for local addresses
[11:53:23]<- messageapplication/vnd.microsoft.card.adaptive
[11:53:23]POST200conversations.replyToActivity
[11:53:23]POST200directline.conversationUpdate
[11:53:23]POST200directline.conversationUpdate
expected and actual results: it should ask what is your name once connected and start conversing
================================================================================mainDialog.ts
import {
BotFrameworkAdapter,
BotTelemetryClient,
RecognizerResult,
StatePropertyAccessor } from 'botbuilder';
import { LuisRecognizer, LuisRecognizerTelemetryClient, QnAMakerResult, QnAMakerTelemetryClient } from 'botbuilder-ai';
import {
DialogContext,
DialogTurnResult,
DialogTurnStatus } from 'botbuilder-dialogs';
import {
ISkillManifest,
SkillContext,
SkillDialog,
SkillRouter } from 'botbuilder-skills';
import {
ICognitiveModelSet,
InterruptionAction,
RouterDialog,
TokenEvents } from 'botbuilder-solutions';
import { TokenStatus } from 'botframework-connector';
import {
Activity,
ActivityTypes } from 'botframework-schema';
import i18next from 'i18next';
import { IOnboardingState } from '../models/onboardingState';
import { CancelResponses } from '../responses/cancelResponses';
import { MainResponses } from '../responses/mainResponses';
import { BotServices } from '../services/botServices';
import { IBotSettings } from '../services/botSettings';
import { CancelDialog } from './cancelDialog';
import { EscalateDialog } from './escalateDialog';
import { OnboardingDialog } from './onboardingDialog';
enum Events {
timeZoneEvent = 'va.timeZone',
locationEvent = 'va.location'
}
export class MainDialog extends RouterDialog {
// Fields
private readonly luisServiceGeneral: string = 'general';
private readonly luisServiceFaq: string = 'faq';
private readonly luisServiceChitchat: string = 'chitchat';
private readonly settings: Partial<IBotSettings>;
private readonly services: BotServices;
private readonly skillContextAccessor: StatePropertyAccessor<SkillContext>;
private readonly onboardingAccessor: StatePropertyAccessor<IOnboardingState>;
private readonly responder: MainResponses = new MainResponses();
// Constructor
public constructor(
settings: Partial<IBotSettings>,
services: BotServices,
onboardingDialog: OnboardingDialog,
escalateDialog: EscalateDialog,
cancelDialog: CancelDialog,
skillDialogs: SkillDialog[],
skillContextAccessor: StatePropertyAccessor<SkillContext>,
onboardingAccessor: StatePropertyAccessor<IOnboardingState>,
telemetryClient: BotTelemetryClient
) {
super(MainDialog.name, telemetryClient);
this.settings = settings;
this.services = services;
this.onboardingAccessor = onboardingAccessor;
this.skillContextAccessor = skillContextAccessor;
this.telemetryClient = telemetryClient;
this.addDialog(onboardingDialog);
this.addDialog(escalateDialog);
this.addDialog(cancelDialog);
skillDialogs.forEach((skillDialog: SkillDialog): void => {
this.addDialog(skillDialog);
});
}
protected async onStart(dc: DialogContext): Promise<void> {
const view: MainResponses = new MainResponses();
const onboardingState: IOnboardingState|undefined = await this.onboardingAccessor.get(dc.context);
if (onboardingState === undefined || onboardingState.name === undefined || onboardingState.name === '') {
await view.replyWith(dc.context, MainResponses.responseIds.newUserGreeting);
} else {
await view.replyWith(dc.context, MainResponses.responseIds.returningUserGreeting);
}
}
protected async route(dc: DialogContext): Promise<void> {
// Get cognitive models for locale
const locale: string = i18next.language.substring(0, 2);
const cognitiveModels: ICognitiveModelSet | undefined = this.services.cognitiveModelSets.get(locale);
if (cognitiveModels === undefined) {
throw new Error('There is no value in cognitiveModels');
}
// Check dispatch result
const dispatchResult: RecognizerResult = await cognitiveModels.dispatchService.recognize(dc.context);
const intent: string = LuisRecognizer.topIntent(dispatchResult);
if (this.settings.skills === undefined) {
throw new Error('There is no skills in settings value');
}
// Identify if the dispatch intent matches any Action within a Skill if so, we pass to the appropriate SkillDialog to hand-off
const identifiedSkill: ISkillManifest | undefined = SkillRouter.isSkill(this.settings.skills, intent);
if (identifiedSkill !== undefined) {
// We have identified a skill so initialize the skill connection with the target skill
const result: DialogTurnResult = await dc.beginDialog(identifiedSkill.id);
if (result.status === DialogTurnStatus.complete) {
await this.complete(dc);
}
} else if (intent === 'l_general') {
// If dispatch result is general luis model
const luisService: LuisRecognizerTelemetryClient | undefined = cognitiveModels.luisServices.get(this.luisServiceGeneral);
if (luisService === undefined) {
throw new Error('The specified LUIS Model could not be found in your Bot Services configuration.');
} else {
const result: RecognizerResult = await luisService.recognize(dc.context);
if (result !== undefined) {
const generalIntent: string = LuisRecognizer.topIntent(result);
// switch on general intents
switch (generalIntent) {
case 'Escalate': {
// start escalate dialog
await dc.beginDialog(EscalateDialog.name);
break;
}
case 'None':
default: {
// No intent was identified, send confused message
await this.responder.replyWith(dc.context, MainResponses.responseIds.confused);
}
}
}
}
} else if (intent === 'q_faq') {
const qnaService: QnAMakerTelemetryClient | undefined = cognitiveModels.qnaServices.get(this.luisServiceFaq);
if (qnaService === undefined) {
throw new Error('The specified QnA Maker Service could not be found in your Bot Services configuration.');
} else {
const answers: QnAMakerResult[] = await qnaService.getAnswers(dc.context);
if (answers !== undefined && answers.length > 0) {
await dc.context.sendActivity(answers[0].answer, answers[0].answer);
} else {
await this.responder.replyWith(dc.context, MainResponses.responseIds.confused);
}
}
} else if (intent === 'q_chitchat') {
const qnaService: QnAMakerTelemetryClient | undefined = cognitiveModels.qnaServices.get(this.luisServiceChitchat);
if (qnaService === undefined) {
throw new Error('The specified QnA Maker Service could not be found in your Bot Services configuration.');
} else {
const answers: QnAMakerResult[] = await qnaService.getAnswers(dc.context);
if (answers !== undefined && answers.length > 0) {
await dc.context.sendActivity(answers[0].answer, answers[0].answer);
} else {
await this.responder.replyWith(dc.context, MainResponses.responseIds.confused);
}
}
} else {
// If dispatch intent does not map to configured models, send 'confused' response.
await this.responder.replyWith(dc.context, MainResponses.responseIds.confused);
}
}
protected async onEvent(dc: DialogContext): Promise<void> {
// Check if there was an action submitted from intro card
if (dc.context.activity.value) {
// tslint:disable-next-line: no-unsafe-any
if (dc.context.activity.value.action === 'startOnboarding') {
await dc.beginDialog(OnboardingDialog.name);
return;
}
}
let forward: boolean = true;
const ev: Activity = dc.context.activity;
if (ev.name !== undefined && ev.name.trim().length > 0) {
switch (ev.name) {
case Events.timeZoneEvent: {
try {
const timezone: string = <string> ev.value;
const tz: string = new Date().toLocaleString(timezone);
const timeZoneObj: {
timezone: string;
} = {
timezone: tz
};
const skillContext: SkillContext = await this.skillContextAccessor.get(dc.context, new SkillContext());
skillContext.setObj(timezone, timeZoneObj);
await this.skillContextAccessor.set(dc.context, skillContext);
} catch {
await dc.context.sendActivity(
{
type: ActivityTypes.Trace,
text: `"Timezone passed could not be mapped to a valid Timezone. Property not set."`
}
);
}
forward = false;
break;
}
case Events.locationEvent: {
const location: string = <string> ev.value;
const locationObj: {
location: string;
} = {
location: location
};
const skillContext: SkillContext = await this.skillContextAccessor.get(dc.context, new SkillContext());
skillContext.setObj(location, locationObj);
await this.skillContextAccessor.set(dc.context, skillContext);
forward = true;
break;
}
case TokenEvents.tokenResponseEventName: {
forward = true;
break;
}
default: {
await dc.context.sendActivity(
{
type: ActivityTypes.Trace,
text: `"Unknown Event ${ ev.name } was received but not processed."`
}
);
forward = false;
}
}
}
if (forward) {
const result: DialogTurnResult = await dc.continueDialog();
if (result.status === DialogTurnStatus.complete) {
await this.complete(dc);
}
}
}
protected async complete(dc: DialogContext, result?: DialogTurnResult): Promise<void> {
// The active dialog's stack ended with a complete status
await this.responder.replyWith(dc.context, MainResponses.responseIds.completed);
}
protected async onInterruptDialog(dc: DialogContext): Promise<InterruptionAction> {
if (dc.context.activity.type === ActivityTypes.Message) {
const locale: string = i18next.language.substring(0, 2);
const cognitiveModels: ICognitiveModelSet | undefined = this.services.cognitiveModelSets.get(locale);
if (cognitiveModels === undefined) {
throw new Error('There is no cognitiveModels value');
}
// check luis intent
const luisService: LuisRecognizerTelemetryClient | undefined = cognitiveModels.luisServices.get(this.luisServiceGeneral);
if (luisService === undefined) {
throw new Error('The general LUIS Model could not be found in your Bot Services configuration.');
} else {
const luisResult: RecognizerResult = await luisService.recognize(dc.context);
const intent: string = LuisRecognizer.topIntent(luisResult);
// Only triggers interruption if confidence level is high
if (luisResult.intents[intent] !== undefined && luisResult.intents[intent].score > 0.5) {
switch (intent) {
case 'Cancel': {
return this.onCancel(dc);
}
case 'Help': {
return this.onHelp(dc);
}
case 'Logout': {
return this.onLogout(dc);
}
default:
}
}
}
}
return InterruptionAction.NoAction;
}
private async onCancel(dc: DialogContext): Promise<InterruptionAction> {
if (dc.activeDialog !== undefined && dc.activeDialog.id !== CancelDialog.name) {
// Don't start restart cancel dialog
await dc.beginDialog(CancelDialog.name);
// Signal that the dialog is waiting on user response
return InterruptionAction.StartedDialog;
}
const view: CancelResponses = new CancelResponses();
await view.replyWith(dc.context, CancelResponses.responseIds.nothingToCancelMessage);
return InterruptionAction.StartedDialog;
}
private async onHelp(dc: DialogContext): Promise<InterruptionAction> {
await this.responder.replyWith(dc.context, MainResponses.responseIds.help);
// Signal the conversation was interrupted and should immediately continue
return InterruptionAction.MessageSentToUser;
}
private async onLogout(dc: DialogContext): Promise<InterruptionAction> {
let adapter: BotFrameworkAdapter;
const supported: boolean = dc.context.adapter instanceof BotFrameworkAdapter;
if (!supported) {
throw new Error('OAuthPrompt.SignOutUser(): not supported by the current adapter');
} else {
adapter = <BotFrameworkAdapter> dc.context.adapter;
}
await dc.cancelAllDialogs();
// Sign out user
// PENDING check adapter.getTokenStatusAsync
const tokens: TokenStatus[] = [];
tokens.forEach(async (token: TokenStatus): Promise<void> => {
if (token.connectionName !== undefined) {
await adapter.signOutUser(dc.context, token.connectionName);
}
});
await dc.context.sendActivity(i18next.t('main.logOut'));
return InterruptionAction.StartedDialog;
}
}
=================================================================================onboardingDialog.ts
import {
BotTelemetryClient,
StatePropertyAccessor,
TurnContext } from 'botbuilder';
import {
ComponentDialog,
DialogTurnResult,
TextPrompt,
WaterfallDialog,
WaterfallStepContext } from 'botbuilder-dialogs';
import { IOnboardingState } from '../models/onboardingState';
import { OnboardingResponses } from '../responses/onboardingResponses';
import { BotServices } from '../services/botServices';
enum DialogIds {
namePrompt = 'namePrompt',
emailPrompt = 'emailPrompt',
locationPrompt = 'locationPrompt'
}
export class OnboardingDialog extends ComponentDialog {
// Fields
private static readonly responder: OnboardingResponses = new OnboardingResponses();
private readonly accessor: StatePropertyAccessor<IOnboardingState>;
private state!: IOnboardingState;
// Constructor
public constructor(botServices: BotServices, accessor: StatePropertyAccessor<IOnboardingState>, telemetryClient: BotTelemetryClient) {
super(OnboardingDialog.name);
this.accessor = accessor;
this.initialDialogId = OnboardingDialog.name;
const onboarding: ((sc: WaterfallStepContext<IOnboardingState>) => Promise<DialogTurnResult>)[] = [
this.askForName.bind(this),
this.finishOnboardingDialog.bind(this)
];
// To capture built-in waterfall dialog telemetry, set the telemetry client
// to the new waterfall dialog and add it to the component dialog
this.telemetryClient = telemetryClient;
this.addDialog(new WaterfallDialog(this.initialDialogId, onboarding));
this.addDialog(new TextPrompt(DialogIds.namePrompt));
}
public async askForName(sc: WaterfallStepContext<IOnboardingState>): Promise<DialogTurnResult> {
this.state = await this.getStateFromAccessor(sc.context);
if (this.state.name !== undefined && this.state.name.trim().length > 0) {
return sc.next(this.state.name);
}
return sc.prompt(DialogIds.namePrompt, {
prompt: await OnboardingDialog.responder.renderTemplate(
sc.context,
OnboardingResponses.responseIds.namePrompt,
<string> sc.context.activity.locale)
});
}
public async finishOnboardingDialog(sc: WaterfallStepContext<IOnboardingState>): Promise<DialogTurnResult> {
this.state = await this.getStateFromAccessor(sc.context);
this.state.name = <string> sc.result;
await this.accessor.set(sc.context, this.state);
await OnboardingDialog.responder.replyWith(
sc.context,
OnboardingResponses.responseIds.haveNameMessage,
{
name: this.state.name
});
return sc.endDialog();
}
private async getStateFromAccessor(context: TurnContext): Promise<IOnboardingState> {
const state: IOnboardingState | undefined = await this.accessor.get(context);
if (state === undefined) {
const newState: IOnboardingState = {
email: '',
location: '',
name: ''
};
await this.accessor.set(context, newState);
return newState;
}
return state;
}
}
=================================================================================dialogBot.ts
import {
ActivityHandler,
BotTelemetryClient,
ConversationState,
EndOfConversationCodes,
Severity,
TurnContext } from 'botbuilder';
import {
Dialog,
DialogContext,
DialogSet,
DialogState } from 'botbuilder-dialogs';
export class DialogBot<T extends Dialog> extends ActivityHandler {
private readonly telemetryClient: BotTelemetryClient;
private readonly solutionName: string = 'samplevirtualassistant';
private readonly rootDialogId: string;
private readonly dialogs: DialogSet;
public constructor(
conversationState: ConversationState,
telemetryClient: BotTelemetryClient,
dialog: T) {
super();
this.rootDialogId = dialog.id;
this.telemetryClient = telemetryClient;
this.dialogs = new DialogSet(conversationState.createProperty<DialogState>(this.solutionName));
this.dialogs.add(dialog);
this.onTurn(this.turn.bind(this));
}
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/tslint/config
public async turn(turnContext: TurnContext, next: () => Promise<void>): Promise<any> {
// Client notifying this bot took to long to respond (timed out)
if (turnContext.activity.code === EndOfConversationCodes.BotTimedOut) {
this.telemetryClient.trackTrace({
message: `Timeout in ${ turnContext.activity.channelId } channel: Bot took too long to respond`,
severityLevel: Severity.Information
});
return;
}
const dc: DialogContext = await this.dialogs.createContext(turnContext);
if (dc.activeDialog !== undefined) {
await dc.continueDialog();
} else {
await dc.beginDialog(this.rootDialogId);
}
await next();
}
}

while deny the camera permission in android it wont ask again

first time application asking permission for the camera, if we deny the permission it won't ask again if we allow its working fine ??
var scanPage = new ZXingScannerPage();
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (cameraStatus != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Camera });
cameraStatus = results[Permission.Camera];
if (cameraStatus == PermissionStatus.Granted)
{
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
else if (cameraStatus == PermissionStatus.Unknown)
{
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
If we deny the camera permission, again its asks while opening camera until we allow the permission.
I wrote a demo about this.
https://github.com/851265601/CheckPermissionDemo
This a GIF of this demo.
Did you check the permission every time when you use the camera function? In this demo, when i click the button ,it will check the permission, then give the result to users, if not give the permission, it will still ask the persmission like the following code.
async void ButtonPermission_OnClicked(object sender, EventArgs e)
{
if (busy)
return;
busy = true;
((Button)sender).IsEnabled = false;
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
await DisplayAlert("Pre - Results", status.ToString(), "OK");
if (status != PermissionStatus.Granted)
{
status = await Utils.CheckPermissions(Permission.Camera);
await DisplayAlert("Results", status.ToString(), "OK");
}
busy = false;
((Button)sender).IsEnabled = true;
}
}
When DisplayAlert was appear, it are used MainApplication for different life cycle
namespace CheckPermissionDemo.Droid
{
//You can specify additional application information in this attribute
[Application]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
//A great place to initialize Xamarin.Insights and Dependency Services!
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed(Activity activity)
{
}
public void OnActivityPaused(Activity activity)
{
}
public void OnActivityResumed(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
}
public void OnActivityStarted(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped(Activity activity)
{
}
}
}

Bot framework in node set sequence message

Developed my bot and now I need all the messages trafficked to be sent to my API, however I have a problem: the order of the messages are not arriving sequentially, how could I solve this?
Every message sent could have an accountant, how could I do this? Or does the bot framework provide this?
Example code
https://gist.github.com/odirleiborgert/8227ff46ca8693307a5373186b9e486c
Or
"use strict"
// ----------------------------------------------------------------------
require('dotenv-extended').load()
// Import packages
const restify = require('restify')
const builder = require('botbuilder')
const api = require('./helpers/api')
// ----------------------------------------------------------------------
/**
* Bot Setup
*/
const port = process.env.port || process.env.PORT || 3978
const server = restify.createServer()
server.listen(port, () => {
console.log(`${server.name} listening to ${server.url}`)
})
const connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
})
const bot = new builder.UniversalBot(connector)
bot.use({
// Usuário envia para o bot
receive: async (event, next) => {
if (event.type == 'message' || event.type == 'conversationUpdate') {
if (process.env.API) {
await api.post.receive(event)
}
}
next()
},
// Bot envia para usuário
send: async (event, next) => {
if (event.type == 'message') {
if (process.env.API) {
await api.post.send(event)
}
}
next()
}
})
bot.use(builder.Middleware.firstRun({
version: 1.0,
dialogId: 'firstRun'
}))
bot.set('storage', new builder.MemoryBotStorage())
// ----------------------------------------------------------------------
// Provider api messages
server.post('/api/messages', connector.listen())
// ----------------------------------------------------------------------
bot.on('conversationUpdate', (session) => {
if (session.membersAdded) {
session.membersAdded.forEach((identity) => {
if (identity.id === session.address.bot.id) {
bot.beginDialog(session.address, 'firstRun')
}
})
}
})
// Add first run dialog
bot.dialog('firstRun', async (session) => {
session.userData.firstRun = true
session.delay(1000)
session.replaceDialog('start')
}).triggerAction({
onFindAction: (context, callback) => {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1)
} else {
callback(null, 0.0)
}
},
matches: [
/^Começar|Comecar$/i
]
})
// ----------------------------------------------------------------------
// Start
bot.dialog('start', require('./dialogs/start'))
.triggerAction({
matches: [
/^start|restart$/i
]
})
// ----------------------------------------------------------------------
// Hello
bot.dialog('hello', require('./dialogs/hello'))
// ----------------------------------------------------------------------
/**
* Dialogs with Intents
*/
const recognizer = new builder.LuisRecognizer(process.env.LUIS_MODEL_URL)
const intents = new builder.IntentDialog({
recognizers: [recognizer]
})
intents.onDefault(require('./dialogs/default'))
// Others
intents.matches('hello', require('./dialogs/hello'))
bot.dialog('/', intents)
// --------------------------------------------------------------------
I created a solution for the moment by creating a counter and date variable and sending the other information along to my API.
let count = 1
bot.use({
receive: async (event, next) => {
count++
if (event.type == 'message' || event.type == 'conversationUpdate') {
event.order_at = new Date()
event.sequence_at = count
await api.post.receive(event)
}
next()
},
send: async (event, next) => {
count++
if (event.type == 'message') {
event.order_at = new Date()
event.sequence_at = count
await api.post.send(event)
}
next()
}
})

Resources