In controller
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
I execute
await Conversation.SendAsync(activity, ()=> MakeJsonRootDialog());
Then in implementation how I get first message sent to BOT?
Object completed contains only fields asked during the conversation:
public static IDialog<JObject> MakeJsonRootDialog(string strDirPath)
{
return Chain.From(() => FormDialog.FromForm(preChatInquery.BuildJsonForm))
.Do(async (context, order) =>
{
try
{
var completed = await order;
await context.PostAsync("Processed your order!");
}
catch (FormCanceledException<JObject> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"You quit on {e.Last}--maybe you can finish next time!";
}
else
{
reply = "Sorry, I've had a short circuit. Please try again.";
}
await context.PostAsync(reply);
}
});
`
The very first thing you need to do when using Chain is to Post the message to the chain, using the PostToChain() method.
Here is an example from the EchoChainDialog sample.
public static readonly IDialog<string> dialog = Chain.PostToChain()
.Select(msg => msg.Text)
Related
Requirement
FormStateModel already contains FIRST input that users types.
Code
Simply I want to put the string that is in activity.Text inside FormStateModel:
private IDialog<FormStateModel> MakeRootDialog(string input)
{
return Chain.From(() => new FormDialog<FormStateModel>(
new FormStateModel() { Question = input },
ContactDetailsForm.BuildForm,
FormOptions.None));
}
=
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(
toBot: activity,
MakeRoot: () => this.MakeRootDialog(activity.Text));
}
else
{
await HandleSystemMessageAsync(activity);
}
var response = this.Request.CreateResponse(HttpStatusCode.OK);
return response;
}
On ConversationUpdate I start conversation simply by asking "Please type your Question:"
private static async Task<Activity> HandleSystemMessageAsync(Activity message)
{
switch (message.Type)
{
case ActivityTypes.DeleteUserData:
break;
case ActivityTypes.ConversationUpdate:
await Welcome(message);
break;
(...)
In that way:
private static async Task Welcome(Activity activity)
{
(...)
reply.Text = string.Format("Hello, how can we help you today? Please type your Question:");
await client.Conversations.ReplyToActivityAsync(reply);
(...)
}
But I can not find a way how to pass it. In this case this exception occurs:
anonymous method closures that capture the environment are not serializable, consider removing environment capture or using a reflection serialization surrogate:
Is there any way around that to populate state model at this step?
Solved by calling RootDialog inside MessagesController, then Calling new FormDialog by context.Call(form, (...));
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
await Conversation.SendAsync(activity, () => new LayerDialog());
}
LayerDialog:
[Serializable]
public class LayerDialog: IDialog<IMessageActivity>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.OnMessageReceivedAsync);
}
private async Task OnMessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var awaited = await result;
FormStateModel model = new FormStateModel();
model.Value = awaited.Text;
var form = new FormDialog<FormStateModel >(model ,
BuildForm , FormOptions.PromptInStart);
context.Call(form , this.AfterResume);
}
I am trying to authenticate a user before proceeding to the rest of the bots functions and have this code to ensure a user exists
[Serializable]
public class ModifyLicenceDialog: IDialog<object>
{
private const string CreateLicence = "Create a new licence";
private const string ModifyEndDate = "Modify end date";
private const string CancelLicence = "Cancel a licence";
public async Task StartAsync(IDialogContext context)
{
if (!CommonConversation.User.IsAuthorized)
{
context.Call(new AuthDialog(), ResumeAfterAuth);
}
}
private async Task ResumeAfterAuth(IDialogContext context, IAwaitable<object> result)
{
await result;
PromptDialog.Choice(context, ResumeAfterChoice, new[] { CreateLicence, ModifyEndDate, CancelLicence },
"What licence function would you like?",
"I am sorry i did not understand that, please select one of the below options");
}
private async Task ResumeAfterChoice(IDialogContext context, IAwaitable<string> result)
{
string selected = await result;
switch (selected)
{
case CreateLicence:
context.Call(new CreateLicenseDialog(), AfterLicenseDialog(CreateLicence));
break;
case ModifyEndDate:
break;
case CancelLicence:
break;
}
}
private ResumeAfter<object> AfterLicenseDialog(IDialogContext context, string licenseEvent)
{
switch (licenseEvent)
{
case CancelLicence:
await context.PostAsync("Auth done");
context.Done(licenseEvent);
break;
}
}
However when my code goes into the AuthDialog when it tries to call the api to get if the user with the provided email exists (specifically at res = await client.GetAsync(uri)) once the api returns a result the bot returns to the parent dialog (ModifyLicenceDialog) and executes ResumeAfterAuth
[Serializable]
public class AuthDialog: IDialog<object>
{
private const string InputEmail = "Please input your email address to continue.";
private int _attempts = 3;
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync(InputEmail);
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
IMessageActivity activity = await result;
string email = activity.Text;
await context.PostAsync("Please wait while we verify this...");
HttpResponseMessage res = new HttpResponseMessage();
HttpClient client = AuthFunctions.Client;
string uri = $"api/v1/auth/licenses/{email}";
res = await client.GetAsync(uri);
if (res.IsSuccessStatusCode)
{
CommonConversation.User = JsonConvert.DeserializeObject<CustomerUser>(await res.Content.ReadAsStringAsync());
await context.PostAsync($"Welcome {CommonConversation.User.FirstName}!");
context.Done(email);
}
else if (_attempts > 0)
{
_attempts--;
await context.PostAsync("I am sorry we do not recognize that email address, lets try again. Please input your email address");
context.Wait(MessageReceivedAsync);
}
else
{
context.Fail(new Exception("I am afraid you are not an authorized user"));
}
}
}
}
Update after some guidance from Nicolas R it turns out that at that point an exception is thrown
the exception is as follows
{"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."}
" at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at SupportHelpdeskBot.Dialogs.AuthDialog.<MessageReceivedAsync>d__3.MoveNext() in C:\\Workspace\\SupportBot\\SupportHelpdeskBot\\SupportHelpdeskBot\\Dialogs\\AuthDialog.cs:line 41"
I have tried System.Net.ServicePointManager.Expect100Continue = false; to stop this from happening but this hasnt work any suggesitons?
There is no error in your implementation: you are completing your AuthDialog here with your context.Done(email):
if (res.IsSuccessStatusCode)
{
CommonConversation.User = JsonConvert.DeserializeObject<CustomerUser>(await res.Content.ReadAsStringAsync());
await context.PostAsync($"Welcome {CommonConversation.User.FirstName}!");
context.Done(email);
}
That's the normal behaviour. What do you want to implement? I think your question is incomplete
i have a basic form. Depending on the choices of the user, it will direct it to various formflows. But i am unable to achieve this. It repeats first formFlow again and again in Microsoft BOT framework ?
//These are two forms that i have initiated. If a count is 1 then it must open first formflow otherwise the second formflow.
internal static IDialog<General> MakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(General.BuildForm));
}
internal static IDialog<ProfileForm> MakeRootDialog1()
{
return Chain.From(() => FormDialog.FromForm(ProfileForm.BuildForm));
}
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message && General.count == 0)
{
await Conversation.SendAsync(activity, MakeRootDialog);
General.count = 1;
}
else if(activity.Type == ActivityTypes.Message && General.count >= 1)
{
await Conversation.SendAsync(activity, MakeRootDialog1);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
I was able to repro the problem but I'm still thinking how that could be solved using Chains in the MessageController.
My suggestion to unblock you is to move the "IF" logic for the Forms into a separate dialog. Something like the following:
Controller
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
RootDialog
[Serializable]
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
if (General.count == 0)
{
General.count = 1;
context.Call(FormDialog.FromForm<General>(General.BuildForm, FormOptions.PromptInStart), async (ctx, formResult) => ctx.Wait(this.MessageReceivedAsync));
}
else if (General.count >= 1)
{
context.Call(FormDialog.FromForm<ProfileForm>(ProfileForm.BuildForm, FormOptions.PromptInStart), async (ctx, formResult) => ctx.Wait(this.MessageReceivedAsync));
}
}
}
This is a personal opinion => I prefer to use Dialogs since once the bot start to grow it's easier to follow the logic and to separate the components.
I'm working on an async http call using HttpClient. The call is made inside an async task. The call is successful and I get a response from the Http call. But when I try to return the response from the task nothing happens, even though I have a breakpoint waiting after the return.
public void ExecuteTask(Foundation.Security.SecurityToken token, Order order)
{
ExecuteTaskAsync(token, order).Wait();
}
public async Task ExecuteTaskAsync(Foundation.Security.SecurityToken token, Order order)
{
if (order != null)
{
log.Info("Starting export of order " + order.ID.ToString());
bool success = await ExportOrder(order, token);
if (!success)
{
log.Error("Failed to export order with ID " + order.ID.ToString());
}
}
}
private async Task<bool> ExportOrder(Order order, Foundation.Security.SecurityToken token)
{
try
{
ResponseObject response = await webService.SendOrder(new SenderInformation(token), new ReceiverInformation(order, token));
if (response.Success && response.Status.Equals("201", StringComparison.OrdinalIgnoreCase))
{
log.Info(String.Format("Order ({0}) was successfully exported"), order.ExternalOrderID);
return true;
}
return false;
}
catch (Exception e)
{
log.Error(String.Format("Exception occured while exporting order ({0})", order.ID), e);
return false;
}
}
Below is the code which does the actual http call. I marked the last functional line with the comment "The code successfully reach this line. After this nothing happens"
public Task<ResponseObject> SendOrder(SenderInformation sender, ReceiverInformation receiver)
{
OrderRequest request = new OrderRequest(sender, receiver);
return ExecuteRequest<OrderRequest, ResponseObject>(request);
}
private async Task<ResponseType> ExecuteRequest<RequestType, ResponseType> (RequestType request)
where RequestType : RequestObject
where ResponseType : class, ResponseObject, new()
{
try
{
using (var client = new HttpClient())
{
string xml = SerializeRequest(request);
HttpContent content = new StringContent(xml);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/xml");
string requestUrl = "URL";
HttpResponseMessage response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
// Parse response
if (response.IsSuccessStatusCode)
{
Stream responseStream = await response.Content.ReadAsStreamAsync();
ResponseType responseObject = DeserializeResponse<ResponseType>(responseStream);
if (responseObject != null)
{
responseObject.Success = true;
return responseObject; //The code successfully reach this line. After this nothing happens
}
else
{
log.Error("Response could not be deserialized");
}
}
else
{
log.Error("Error during request, got status code " + response.StatusCode);
}
}
}
catch (Exception e)
{
log.Error("Something went wrong!", e);
}
return new ResponseType() { Success = false };
}
The problem is on this line:
ExecuteTaskAsync(token, order).Wait();
This causes a deadlock: the awaits in the called method can't resume because the UI thread is blocked.
When you use async code, you must use it all the way; never wait synchronously for an async task to complete.
My problem is the following:
On the UI thread I have a button event, from where I call a service method:
private async void RefreshObjectsButton_Click(object sender, EventArgs e)
{
var objectService = new ObjectService();
var objects = await objectService.GetObjects(UserInfo.Token);
}
The service method is:
public class ObjectService : ServiceClientBase, IObjectService
{
public async Task<ObservableCollection<ObjectViewModel>> GetObjects(string token)
{
var response = await GetAsync<ObservableCollection<HookViewModel>>("uri_address", token);
return response;
}
}
And the GetAsync method which is implemented in ServiceClientBase:
public async Task<T> GetAsync<T>(string uri, string token)
{
using (var client = CreateClient())
{
try
{
HttpResponseMessage response = new HttpResponseMessage();
response = await client.GetAsync(uri);
T retVal = default(T);
if (response.IsSuccessStatusCode)
{
retVal = await response.Content.ReadAsAsync<T>();
}
return retVal;
}
catch (Exception ex)
{
//TO DO log exception
return default(T);
}
}
}
When execution reaches GetAsync<T>(), the request is sent, I get a result and retVal contains a list of values. However after GetAsync<T>() execution ends, GetObjects() will not continue its execution anymore. I believe it's a deadlock as explained here. However using the advices seen in the previous link didn't resolve my issue. It's clearly I'm missing something here.
Can someone explain why is this deadlock happening, and maybe provide some further advice in resolving this issue?