I am trying to receive an int number (1-4) via Backchannel and then hand it over to the first dialog.
My message controller looks like this:
private int option = 1;
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
try
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity isTypingReply = activity.CreateReply();
isTypingReply.Type = ActivityTypes.Typing;
await connector.Conversations.ReplyToActivityAsync(isTypingReply);
await Conversation.SendAsync(activity, () => new Dialogs.MenuDialog(option));
}
catch (Exception e)
{
//SendEmail(e);
}
}
else
{
await HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private async Task HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
else if (message.Type == ActivityTypes.Event && message.Name == "option")
{
// var reply = message.CreateReply();
//reply.Text = message.Value.ToString();
// ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl));
// await connector.Conversations.ReplyToActivityAsync(reply);
if (message.Value.ToString() == "1")
{
option = 1;
}
else if (message.Value.ToString() == "2")
{
option = 2;
}
else if (message.Value.ToString() == "3")
{
option = 3;
}
else if (message.Value.ToString() == "4")
{
option = 4;
}
else
{
option = 1;
}
}
return;
}
The Backchannel method is called right and the option value is set when I print it at the end of the function.
But then when the first message comes the Bot is always using the default "1" value.
It was working before but now it stopped working and I don't understand why.
private int option = 1;
Is scoped to the MessageController, and will be refreshed on every call. You can use PrivateConversationData to preserve the 'option' between the Event and Message calls:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
//retrieve the option value before processing the message
string optionValue = string.Empty;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(CancellationToken.None);
optionValue = botData.PrivateConversationData.GetValue<string>("option");
}
await Conversation.SendAsync(activity, () => new ParameterizedRootDialog(optionValue));
}
else if (activity.Type == ActivityTypes.Event)
{
var eventActivity = activity.AsEventActivity();
if (string.Equals(eventActivity.Name, "option", StringComparison.InvariantCultureIgnoreCase))
{
//save the option into PrivateConversationData
string optionValue = eventActivity.Value.ToString();
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(CancellationToken.None);
botData.PrivateConversationData.SetValue("option", optionValue);
await botData.FlushAsync(CancellationToken.None);
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
Also of note: with this method, it is not necessary to send in the option to the dialog as a parameter. You could retrieve the value from within the dialog itself, using IDialogContext.PrivateConversationData. Like this:
var optionFromContext = context.PrivateConversationData.GetValue<string>("option");
Related
I am creating an application for myself, and I need my service to work when the device is rebooted. I did it here is my code.
namespace Corporate_messenger.Droid.Broadcast
{
[BroadcastReceiver(Name = "com.companyname.corporate_messenger.BootReceiver", Enabled = true, Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
private void Start1(Context context)
{
Intent mycallIntent = new Intent(context, typeof(MainActivity));
mycallIntent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(mycallIntent);
}
public override void OnReceive(Context context, Intent intent)
{
try
{
var intentService = new Intent(context, typeof(NotoficationService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
SpecialData.RestartResponse = true;
context.StartForegroundService(intentService);
}
else
{
context.StartService(intentService);
// Flag_On_Off_Service = true;
}
}
catch(Exception ex)
{
Log.Error("MyLog", ex.Message);
}
} // OnReceive
}
}
I also requested permissions to work with folders and microphone.
My cod - Permission
public async Task Permission()
{
var PermissionsStrorage_Write = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
var PermissionsInternet = await Permissions.CheckStatusAsync<Permissions.NetworkState>();
var PermissionsStrorage_Read = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
var PermissionsRecord = await Permissions.CheckStatusAsync<Permissions.Microphone>();
if (PermissionsInternet != PermissionStatus.Granted)
{
PermissionsInternet = await Permissions.RequestAsync<Permissions.NetworkState>();
}
if (PermissionsRecord != PermissionStatus.Granted)
{
PermissionsRecord = await Permissions.RequestAsync<Permissions.Microphone>();
}
if (PermissionsStrorage_Write != PermissionStatus.Granted && PermissionsStrorage_Read != PermissionStatus.Granted)
{
PermissionsStrorage_Write = await Permissions.RequestAsync<Permissions.StorageWrite>();
PermissionsStrorage_Read = await Permissions.RequestAsync<Permissions.StorageRead>();
}
if (PermissionsStrorage_Write != PermissionStatus.Granted && PermissionsStrorage_Read != PermissionStatus.Granted)
{
return;
}
}
Result code:
But I ran into a problem, which is that for my service to work correctly on some devices, two checkboxes are required. Here's a picture
Now I don't understand how to ask the user about these permissions so that he doesn't have to go into the settings himself. Perhaps the application could open this page on its own.Basically , this problem occurs on xiaomi phone. At the moment I am writing an application for android. But xamarin allows you to write code for iOS, so in the future I will also add such functions there.
here is the answer to this question
private void SetSetting()
{
// Manufacturer (Samsung)
var manufacturer = DeviceInfo.Manufacturer.ToLower();
switch (manufacturer)
{
case "xiaomi":
SetPermission("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity");
break;
case "huawei":
SetPermission("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity");
break;
case "samsung":
if(Battery.EnergySaverStatus == EnergySaverStatus.On)
SetPermission("com.samsung.android.lool", "com.samsung.android.sm.battery.ui.BatteryActivity");
break;
}
}
private void SetPermission(string param1,string param2)
{
try
{
Intent intent = new Intent();
intent.SetComponent(new ComponentName(param1, param2));
// intent.SetComponent(new ComponentName("com.miui.securitycenter", "com.miui.appmanager.ApplicationsDetailsActivity"));
intent.PutExtra("package_name", PackageName);
StartActivity(intent);
}
catch (Exception anfe)
{
}
}
How to get into settings on other devices
How to start Power Manager of all android manufactures to enable background and push notification?
Only I just don't understand how to find out the status of the flag
This is code from my handler:
public class MoeZdravjeStatusCodeHandler : DelegatingHandler
{
public MoeZdravjeStatusCodeHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
public IMojLekDataStore<Object> MojLekDataStore => DependencyService.Get<IMojLekDataStore<Object>>();
public bool flag=true;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage();
response = await base.SendAsync(request, cancellationToken);
int status_code = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
return response;
}
else if(status_code == 401 && DependencyService.Get<ISharedFunctions>().GetUserValidation() == true )
{
try
{
if (flag)
{
flag = false;
string date = DateTime.Today.ToString("yyyy-MM-dd") + "MoeZdravje";
string password = Helpers.ServiceHelper.HashPassword(date).ToString();
LoginMoeZdravje log = new LoginMoeZdravje();
log.EZBO = DependencyService.Get<ISharedFunctions>().GetPatient().Ezbo.ToString();
log.PASSWORD = password;
log.CONFIRMED_USER = 1;
var response_token = await MojLekDataStore.GetMoeZdravjeToken(log);
if (response_token != null && !String.IsNullOrEmpty(response_token.Token))
{
flag = true;
DependencyService.Get<ISharedFunctions>().SaveMoeZdravjeToken(response_token.Token);
await Application.Current.MainPage.DisplayAlert("", "ВАШИОТ ТОКЕН Е ОСВЕЖЕН", AppResources.BTNOK);
Application.Current.MainPage = new NavigationPage(new MainMenu());
}
else
{
flag = true;
await Application.Current.MainPage.DisplayAlert("", AppResources.SERVER_ERROR, AppResources.BTNOK);
}
}
else
{
CancellationTokenSource source = new CancellationTokenSource();
cancellationToken = source.Token;
source.Cancel();
source.Dispose();
}
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally
{
}
}
return response;
}
I want when come to await MojLekDataStore.GetToken(log); block every async Task until finish this request because with this request i get a new token from my Api and need to save that token and call the requests to get the data with the new token. I have a tabbedPage with 4 async Tasks and this handler called two times for get a new token but i need one and to start work with the new token.
EDIT
I added CancellationTokenSource source = new CancellationTokenSource();
but i dont know if this could stop other 3 async task ? The flag is used when first 401status_code request come .
1 ) Its a weather Api and i want it to display no internet connection when there is no internet connected to the device.
public async void method()
{
Indicator.IsRunning = true;
Weather weather = await GetWeather(nameplace.Text);
if (weather != null)
{
if (weather.message == "city not found")
{
txtLocation.Text = "city not found";
}
else
{
Location.Text = weather.Title;
Temperature.Text = weather.Temperature;
Temperature.Text += "°";
txtWind.Text = weather.Wind;
Humidity.Text = weather.Humidity;
Sunrise.Text = weather.Sunrise;
Sunset.Text = weather.Sunset;
double condition = Convert.ToDouble(weather.Condition);
if (condition >= 0 && condition < 30)
{
Condition.Text = "Clear Sky";
}
else if (condition >= 30)
{
Condition.Text = "Cloudy";
}
}
}
else
{
await DisplayAlert("Alert", "Sorry ! No internet connection","Ok")`
}
}
3) // Below in a getservice function i have
if (Plugin.Connectivity.CrossConnectivity.Current.IsConnected)
{
var response = await client.GetAsync(QueryString);
// pQueryString is the http request
_httpStatusCode = response.StatusCode.ToString();
if (response == null)
{
return "Sorry! No record Found.";
}
else if (response != null)
{
string json = response.Content.ReadAsStringAsync().Result;
data = JsonConvert.DeserializeObject(json);
}
}
else
{
data = null;
}
return data;
4) i have return null and put a condition in there so that it can display an alert saying no internet connection
It is possible that your async void method messes with the Thread context, and that you end up not calling your code in the ui thread.
You could try this:
Xamarin.Forms.Device.BeginInvokeOnMainThread(
() => DisplayAlert("Alert", "Sorry ! No internet connection","Ok"));
Also it's a bad design to put UI code in your service layer.
Your should call your DisplayAlert code in your ViewModel not in your service.
I'm trying to create some animation during the time when I fetch the data from a server. "Typing" activity seems to be reasonable but it works only for ~4 seconds :
Activity reply = activity.CreateReply();
reply.Type = ActivityTypes.Typing;
reply.Text = null;
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(reply);
I was trying to do async listening:
while (!_fetchEnded)
{
await connector.Conversations.ReplyToActivityAsync(reply);
Thread.Sleep(3000);
}
But bot it creates laggy behaviour. Is there a possibility to set the duration of "typing" activity or another way around to prevent turning the typing on and off?
Typing is displayed only a few seconds by default. You can force the display typing indicator longer by sending again typing events at a lower frequency.
Implementation example, where it will send events every 2 seconds, for 30 seconds max:
public async Task<HttpResponseMessage> Post([FromBody]Microsoft.Bot.Connector.Activity activity, CancellationToken token)
{
// Send Typing messages
var typingCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var typingTask = SendTypingActivityUntilCancellation(activity, TimeSpan.FromSeconds(2), typingCancellation.Token);
try
{
// Activity treatment
if (activity.Type == ActivityTypes.Message)
{
// ...
}
else if (activity.Type == ActivityTypes.Event && activity.ChannelId == ChannelEnum.directline.ToString())
{
// ...
}
typingCancellation.Cancel();
await typingTask;
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception e)
{
typingCancellation.Cancel();
await typingTask;
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
public async Task SendTypingActivityUntilCancellation(Activity activity, TimeSpan period, CancellationToken cancellationtoken)
{
try
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity isTypingReply = activity.CreateReply();
isTypingReply.Type = ActivityTypes.Typing;
do
{
if (cancellationtoken.IsCancellationRequested == false)
{
await connector.Conversations.ReplyToActivityAsync(isTypingReply);
}
// Check again if token has not been canceled during the reply delay
if (cancellationtoken.IsCancellationRequested == false)
{
await Task.Delay(period);
}
}
while (cancellationtoken.IsCancellationRequested == false);
}
catch (OperationCanceledException)
{
//nothing to do.
}
}
I am developing a chatbot using Microsoftt Bot Framework and LUIS cognitive services.
I want a initial welcome message something like "Hello user how are you!" as soon as my bot starts.
anything can be done here in MessageController
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
Trace.TraceInformation($"Type={activity.Type} Text={activity.Text}");
if (activity.Type == ActivityTypes.Message)
{
//await Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync(activity, () => new ContactOneDialog());
await Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync(activity, () =>
new ExceptionHandlerDialog<object>(new ShuttleBusDialog(), displayException: true));
//await Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync(activity, () => new ShuttleBusDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
return response;
}
You might want to explore sending the message as part of the ConversationUpdate event. Update your HandleSystemMessage method so it looks like the following:
private async Task HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl));
var reply = message.CreateReply();
reply.Text = "Hello user how are you?"
await client.Conversations.ReplyToActivityAsync(reply);
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
}
In the new version HandleSystemMessage is no more async and it returns an Activity, so this is what worked for me:
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
if (message.MembersAdded.Any(o => o.Id == message.Recipient.Id))
{
ConnectorClient connector = new ConnectorClient(new Uri(message.ServiceUrl));
Activity reply = message.CreateReply("I am your service provider virtual assistant, How can I help you today? ");
connector.Conversations.ReplyToActivityAsync(reply);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
Note the following things:
Since HandleSystemMessage is not async you cannot "await" to reply.
Use the recipient id check to avoid duplicated welcome messages
Why modify the HandleSystemMessage method, when you could simply handle it right in the Post method?
Thus, you don't have to mess with creating a new connector and using the unfamiliar connector.Conversations.ReplyToActivityAsync(reply) method.
You can simply start your root dialog, same as you would do in reply to a message:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
// ... some code ...
// this will also handle the beginning of the conversation -
// that is when activity.Type equals to ConversationUpdate
if (activity.Type == ActivityTypes.Message
|| activity.Type == ActivityTypes.ConversationUpdate)
{
// Because ConversationUpdate activity is received twice
// (once for the Bot being added to the conversation, and the 2nd time -
// for the user), we have to filter one out, if we don't want the dialog
// to get started twice. Otherwise the user will receive a duplicate message.
if (activity.Type == ActivityTypes.ConversationUpdate &&
!activity.MembersAdded.Any(r => r.Name == "Bot"))
return response;
// start your root dialog here
await Microsoft.Bot.Builder.Dialogs.Conversation.SendAsync(activity, () =>
new ExceptionHandlerDialog<object>(new ShuttleBusDialog(), displayException: true));
}
// handle other types of activity here (other than Message and ConversationUpdate
// activity types)
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
return response;
}
When I used "WebChat" channel, the below code worked fine. ChatBot could able to initiate the greeting message and also there was no duplicates!
case ActivityTypes.ConversationUpdate:
IConversationUpdateActivity update = activity;
var client = new ConnectorClient(new Uri(activity.ServiceUrl), new MicrosoftAppCredentials());
if (update.MembersAdded != null && update.MembersAdded.Any())
{
foreach (var newMember in update.MembersAdded)
{
if (newMember.Id == activity.Recipient.Id)
{
var reply = activity.CreateReply();
reply.Text = $"Welcome {newMember.Name}!";
await client.Conversations.ReplyToActivityAsync(reply);
}
}
}
break;