BOT Framework emulator stopped rendering HTML - emulation

Bot Framework suddenly stopped rendering html, I have attached screenshot. What can be wrong? I am using v3.5.31 which is the latest one. I have used Bot Application template which is bydefault available when creating new BOT project.
Below is the code i have written
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
PromptDialog.Text(context, resumeAfter, "Enter your name", "Didn't get that",
3
);
}
private async Task resumeAfter(IDialogContext context, IAwaitable<string> result)
{
string answer = await result;
await context.PostAsync($"Hello <i><b>{answer}</b></i>");
}
}

This was reported here. and it looks like it's by design in the WebChat control (that is what the Emulator uses behind the scenes). See the change in the WebChat control: https://github.com/Microsoft/BotFramework-WebChat/commit/a5cd8cffb7a97fb925e4078dbe1085694fa92f80

Related

Calling Hub method from a controller's action in ASPNetCore MVC application using SignalR

I would like the ASPNetCore2.0 webapp I'm working on to send a notification to specific users using SignalR. I would like to call the hub's method from another controller's action (as opposed to a client's JS call).
I have learned that this is not how SignalR is intended to be used, but I've found many users who had the same 'desire' and also some solutions.
I have checked several proposed solutions, but the simplest and cleaner seemed to be the accepted answer to this post: Get Hub Context in SignalR Core from within another object.
So I gave it a go, and I get no errors at all. The server's output is error-free, and so are the browser's console and network tabs (I'm using Chrome). When debugging, the flow is smooth and the program does exactly what it should do... except the users don't get any notification...
Do any of you spot the problem?
I created a class that contains the shared methods for the hub:
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
public class HubMethods
{
private readonly IHubContext<PostsHub> _hubContext;
public HubMethods(IHubContext<PostsHub> hubContext)
{
_hubContext = hubContext;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubContext.Clients.Users(users).SendAsync("Notify", sender, postId);
}
}
}
Then I created a hub:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Hubs
{
[Authorize]
public class PostsHub : Hub
{
private HubMethods _hubMethods;
public PostsHub(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
public async Task Notify(string postId, string sender, List<string> users)
{
await _hubMethods.Notify(postId, sender, users);
}
}
}
Added these bits to Startup's ConfigureServices method:
[...]// Services before these...
services.AddSignalR();
services.AddScoped<HubMethods>();
services.AddMvc();
And Startup's Configure method:
app.UseSignalR(routes =>
{
routes.MapHub<PostsHub>("/postshub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Then these lines to the view:
<script src="~/lib/signalr/signalr.js"></script>
#await Html.PartialAsync("_NotifyScriptsPartial")
And this is "_NotifyScriptsPartial.cshtml":
<script>
var connection = new signalR.HubConnectionBuilder().withUrl('/PostsHub').build();
connection.on('Notify', function (sender, postId) {
var post = postId;
var sentBy = sender;
var content = '<a href=\'#\' class=\'close\' data-dismiss=\'alert\' aria-label=\'close\'>×</a>' +
'You just received a new comment from <strong>' +
sentBy + '</strong>. Click <a href = \'#\' class=\'alert-link\' >here</a> to view the post.'
var alert = document.createElement('div');
alert.classList.add('alert', 'alert-info', 'alert-dismissible');
alert.html(content);
document.getElementById('pageContent').appendChild(alert);
});
</script>
Finally, in the controller that is supposed to send the notification, I added these:
public class PostsController : Controller
{
private readonly HubMethods _hubMethods;
public PostsController(HubMethods hubMethods)
{
_hubMethods = hubMethods;
}
// POST: Create a new post
[Authorize]
[HttpPost]
public async Task<IActionResult> Create(DetailsModel model, List<string> readers)
{
if (ModelState.IsValid)
{
// Do stuff here... including creating the newPostId, userId and receipients variables used below
await _hubMethods.Notify(newPostId, userId, receipients);
// Do more stuff and eventually...
return View();
}
}
}
Any idea?
In Asp.Net Core 2.1 I can use hub like this, It solves my problem, also You used like this in your controller. Hope it helps.
public class SomeController : Controller
{
private readonly IHubContext<MyHub> _myHub;
public SomeController (IHubContext<MyHub> myHub)
{
_myHub = myHub;
}
public void SomeAction()
{
//for your example
_myHub.Clients.All.SendAsync("Notify", "data");
}
}
I can get the "data" text from browser's console. If you use jQuery in your project, add those codes between jQuery(document).ready(function () { }); because you tried to load a partial html and I think your code needs to run after ready() event. Sorry If I misunderstood you.

Azure Bot fails after a time [duplicate]

Good day everyone,
I'm creating a chatbot for my company and I started with the samples on github and the framework docs.
We decided to host it on Azure and added LUIS and Table Storage to it. The Bot runs fine locally in Botframework Emulator, but on Azure (WebChat, Telegram) it will only run for approximatly an hour to an hour and fifteen minutes, if no one tries to communicate with the bot. After this period of time, the bot will just run into an internal server error. When you ask the bot something, you can stretch this time window (For how long I don't know and why I don't know either, sorry).
In Azure "Always On" is set to true.
I'm really frustrated at this point, because I cannot find the problem and I'm pretty sure there must be something wrong with my code, because I don't properly understand the framework. I'm still a beginner with Azure, C# and Bot Framework.
Also I have already read everything on "internal server error's" on here and github. Also tried Debugging, even with extra Debbug options in VS. We have not tried Application Insights yet.
At the moment I'm doing everything with the LUIS Dialog which calls / Forwards to other IDialogs:
[LuisIntent(Intent_Existens)]
public async Task ExistensOf(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result)
{
var existens = new ExistensDialog();
var messageToForward = await message;
if (result.Entities.Count == 1)
{
messageToForward.Value = result.Entities[0].Entity;
await context.Forward(existens, AfterDialog, messageToForward);
}
else
{
context.Wait(this.MessageReceived);
}
}
I know that "Value" is for CardActions, but I don't know how else I could pass Entities to the child dialog.
[Serializable]
public class ExistensDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Text.Contains("specificWord"))
{
await context.Forward(new ExistensHU(), AfterDialog, message);
}
else
{
await context.Forward(new ExistensBin(), AfterDialog, message);
}
}
private async Task AfterDialog(IDialogContext context, IAwaitable<object> result)
{
context.Done<object>(null);
}
}
then:
[Serializable]
internal class ExistensHU : IDialog<object>
{
private Renamer renamer = new Renamer(); // Just for renaming
private ExternalConnection ec = new ExternalConnection(); //Just a HTTP connection to a WebApp to get data from it
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
const string apiCallURL = "some/API/"; // ExternalConnection...
var message = await result;
string nameHU = renamer.RemoveBlanks(message.Value.ToString());
StringBuilder answerBuilder = new StringBuilder();
var name = ec.CreateSingleAPIParameter("name", nameHU);
Dictionary<string, string> wms = await ec.APIResultAsDictionary(apiCallURL, name);
foreach (var item in wms)
{
if (item.Key.Equals("none") && item.Value.Equals("none"))
{
answerBuilder.AppendLine($"Wrong Answer");
}
else
{
answerBuilder.AppendLine($"Correct Answer");
}
}
await context.PostAsync(answerBuilder.ToString());
context.Done<object>(null);
}
}
That's basically every Dialog in my project.
Also I have an IDialog which looks like this:
[Serializable]
public class VerificationDialog : IDialog<object>
{
[NonSerializedAttribute]
private readonly LuisResult _luisResult;
public VerificationDialog(LuisResult luisResult)
{
_luisResult = luisResult;
}
public async Task StartAsync(IDialogContext context)
{
var message = _luisResult.Query;
if (!message.StartsWith("Wie viele"))
{
context.Call(new ByVerificationDialog(_luisResult), AfterDialog);
}
else
{
context.Call(new CountBinsByVerification(_luisResult), AfterDialog);
}
}
private async Task AfterDialog(IDialogContext context, IAwaitable<object> result)
{
context.Done<object>(null);
}
}
I don't know if I'm allowed to pass the luisResult like this from BasicLuisDialog. This could be the issue or one of the issues.
Basically that's it and I'm still getting used to the framework. I'm not expecting an absolute answer. Just hints/tips and advice how to make everything better.
Thanks in advance!
If you are using the .NET SDK version 3.14.0.7. There is currently a bug we are tracking in this version. There has been a number of reports and we are actively investigating. Please try downgrading to 3.13.1. This should fix the issue for you until we can release a new version.
for reference we are tracking the issue on these GitHub issues:
https://github.com/Microsoft/BotBuilder/issues/4322
https://github.com/Microsoft/BotBuilder/issues/4321
Update 3/21/2018:
We have pushed a new version of the SDK which includes a fix for this issue https://www.nuget.org/packages/Microsoft.Bot.Builder/3.14.1.1
Internal error usually means exceptions in .NET application.
Use AppDomain.CurrentDomain.UnhandledException to receive all unhandled exceptions and log them somewhere (consider using Application Insights).
After you investigate logged information fix that.

How does navigation work with LUIS subdialogs?

I have a question... Unfortunately all the samples on the web are too shallow and don't really cover this well:
I have a RootDialog that extends the LuisDialog. This RootDialog is responsible for figuring out what the user wants to do. It could be multiple things, but one of them would be initiating a new order. For this, the RootDialog would forward the call to the NewOrderDialog, and the responsibility of the NewOrderDialog would be to figure out some basic details (what does the user want to order, which address does he like to use) and finally it will confirm the order and return back to the RootDialog.
The code for the RootDialog is very simple:
[Serializable]
public class RootDialog : LuisDialog<object>
{
public RootDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
{
}
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
await context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);
context.Wait(MessageReceived);
}
private async Task OnPlaceOrderIntentCompleted(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync("Your order has been placed. Thank you for shopping with us.");
context.Wait(MessageReceived);
}
}
I also had some code in mind for the NewOrderDialog:
[Serializable]
public class NewOrderDialog : LuisDialog<object>
{
private string _product;
private string _address;
public NewOrderDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"], domain: "westus.api.cognitive.microsoft.com")))
{
}
[LuisIntent("Order.RequestedItem")]
public async Task RequestItemIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Item", out item))
{
_product = item.Entity;
await context.PostAsync($"Okay, I understood you want to order: {_product}.");
}
else
{
await context.PostAsync("I couldn't understand what you would like to buy. Can you try it again?");
}
context.Wait(MessageReceived);
}
[LuisIntent("Order.AddedAddress")]
public async Task AddAddressIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Address", out item))
{
_address = item.Entity;
await context.PostAsync($"Okay, I understood you want to ship the item to: {_address}.");
}
else
{
await context.PostAsync("I couldn't understand where you would like to ship the item. Can you try it again?");
}
context.Wait(MessageReceived);
}
}
The code as listed doesn't work. Upon entering the Order.Place intent, it immediately executes the 'success' callback, and then throws this exception:
Exception: IDialog method execution finished with multiple resume handlers specified through IDialogStack.
[File of type 'text/plain']
So I have a few questions:
How do I solve the error that I get?
How can I, upon entering the NewOrderDialog, check if we already know what the product and address is, and if not prompt them for the correct info?
Why does the NewOrderDialog get closed even though I don't call anything like context.Done()? I only want it to be closed when all the info has been gathered and the order has been confirmed.
So the first issue is that you are doing a context.Forward and a context.Wait in "Order.Place", which by definition is wrong. You need to choose: forward to a new dialog or wait in the current. Based on your post, you want to forward, so just remove the Wait call.
Besides that, you have 1 LUIS dialog and you are trying to forward to a new LUIS dialog... I have my doubts if that will work on not; I can imagine those are two different LUIS models otherwise it will be just wrong.
Based on your comment, I now understand what you are trying to do with the second dialog. The problem (and this is related to your second question) is that using LUIS in tha way might be confusing. Eg:
user: I want to place an order
bot => Forward to new dialog. Since it's a forward, the activity.Text will likely go to LUIS again (to the model of the second dialog) and nothing will be detected. The second dialog will be in Wait state, for user input.
Now, how the user will know that he needs to enter an address or a product? Where are you prompting the user for them? See the issue?
Your third question I suspect is a side effect of the error you are having in #1, which I already provided the solution for.
If you clarify a bit more I might be even more helpful. What you are trying to do with LUIS in the second dialog it doesn't look ok, but maybe with an explanation might have sense.
A usual scenario would be: I get the intent from LUIS ("Order.Place") and then I start a FormFlow or a set of Prompts to get the info to place the order (address, product, etc) or if you want to keep using LUIS you might want to check Luis Action Binding. You can read more on https://blog.botframework.com/2017/04/03/luis-action-binding-bot/.
Do you know about the Bing Location Control for Microsoft Bot Framework? It can be used to handle the 'which address does he like to use' part in obtaining and validating the user's address.
Here is some sample code:
[LuisModel("xxx", "yyy")]
[Serializable]
public class RootDialog : LuisDialog<object>
{
...
[LuisIntent("Find Location")]
public async Task FindLocationIntent(IDialogContext context, LuisResult result)
{
try
{
context.Call(new FindUserLocationDialog(), ResumeAfterLocationDialog);
}
catch (Exception e)
{
// handle exceptions
}
}
public async Task ResumeAfterLocationDialog(IDialogContext context, IAwaitable<object> result)
{
var resultLocation = await result;
await context.PostAsync($"Your location is {resultLocation}");
// do whatever you want
context.Wait(this.MessageReceived);
}
}
And for "FindUserLocationDialog()", you can follow the sample from Line 58 onwards.
Edit:
1) Can you try using:
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, context.Activity, CancellationToken.None);
// or this
// context.Call(new NewOrderDialog(), OnPlaceOrderIntentCompleted);
}
2) I would say it depends on how you structured your intent. Does your "Order.Place" intent include entities? Meaning to say, if your user said "I want to make an order of Product X addressed to Address Y", does your intent already pick up those entities?
I would suggest that you check the product and address under your "Order.Place" intent. After you obtained and validated the product and address, you can forward it to another Dialog (non-LUIS) to handle the rest of the order processing.
[LuisIntent("Order.Place")]
public async Task PlaceOrderIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation item;
if (result.TryFindEntity("Item", out item))
{
_product = item.Entity;
}
if (result.TryFindEntity("Address", out item))
{
_address = item.Entity;
}
if (_product == null)
{
PromptDialog.Text(context, this.MissingProduct, "Enter the product");
}
else if (_address == null)
{
PromptDialog.Text(context, this.MissingAddress, "Enter the address");
}
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
private async Task MissingProduct(IDialogContext context, IAwaitable<String> result)
{
_product = await result;
// perform some validation
if (_address == null)
{
PromptDialog.Text(context, this.MissingAddress, "Enter the address");
}
else
{
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
}
private async Task MissingAddress(IDialogContext context, IAwaitable<String> result)
{
_address = await result;
// perform some validation
// both product and address present
context.Forward(new NewOrderDialog(), OnPlaceOrderIntentCompleted, **object with _product and _address**, CancellationToken.None);
}
Something along these lines. Might need to include try/catch.
3) I think it has got to do with your 'context.Wait(MessageReceived)' in 'Order.Place' LUIS intent

Microsoft Bot Framework - Proactive message, suspend current dialog

I've created a simple news bot which sends updates to a user every 24 hours. I've created a callback controller which handles requests from an external service, processes the resumptionCookie, then sends a carousel of articles with two buttons to the user. One of the buttons opens a browser window, the other should trigger a new dialog (OptionsDialog).
Is this implementation correct? and is it possible to suspend any active dialog, whilst the user interacts with the news article message? For example, if i'm going through a particular dialog, then suddenly I get a news alert, is it possible to suspend the current dialog, to allow the user to update the options of the alerts (almost like the news alert is outside the normal dialog flow), then once they've finished they'll return to the previous dialog. Hopefully, the question is clear enough. Any help will be greatly appreciated.
public class CallbackController : ApiController
{
public async Task<IHttpActionResult> Post(ResumptionCookie resumptionCookie)
{
var activity = (Activity)resumptionCookie.GetMessage();
var reply = activity.CreateReply();
reply.Text = "We found 7 news articles that match your criteria";
reply.Attachments = new List<Attachment>
{
new ThumbnailCard
{
Buttons = new List<CardAction>
{
new CardAction(ActionTypes.OpenUrl, "BBC", null, "http://www.bbc.co.uk"),
new CardAction(ActionTypes.PostBack, "Update Options", null, "Update Options")
}
}.ToAttachment()
};
var client = new ConnectorClient(new Uri(activity.ServiceUrl));
await client.Conversations.ReplyToActivityAsync(reply);
return Ok(new { success = true });
}
}
This is my Main dialog
[Serializable]
public class MainDialog : IDialog
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(ProcessMessage);
}
private async Task ProcessMessage(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var response = await result;
if (response.Text.Equals("Update Options", StringComparison.OrdinalIgnoreCase))
{
context.Call(new OptionsDialog(), FinishMainDialog);
}
else
{
PromptDialog.Confirm(context, ProcessChoice, "Do you wish to continue?");
}
}
private async Task ProcessChoice(IDialogContext context, IAwaitable<bool> result)
{
var choice = await result;
if(choice)
{
context.Call(new DialogOne(), FinishMainDialog);
}
else
{
context.Done(true);
}
}
private async Task FinishMainDialog(IDialogContext context, IAwaitable<object> result)
{
context.Done(true);
}
}
Here is my frequency dialog
[Serializable]
public class OptionsDialog : IDialog
{
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("You can update your to options here");
context.Wait(ProcessMessage);
}
public async Task ProcessMessage(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
await context.PostAsync("Hello, World!");
context.Done(true);
}
}

Internet Connectivity Listener in Xamarin.Forms

I am new in Xamarin.Forms and I want to check internet connectivity status in iOS and Android app. In fact using CrossConnectivity Plugins I am able to check internet connectivity successfully but It is not working as Listener. For example, When I open my app and internet connection is not there then it shows me message that "No internet connection" and now if I ON my mobile data then also it shows me same message. I am using below code for this:
string isConnected=CrossConnectivity.Current.IsConnected?"Connected":"No Connection";
My app is not able to listen the changing status of internet connectivity in middle something.
Using the plugin CrossConnectivity, you need to listen to changes via the event ConnectivityChanged, so in your page, or your App class, add this code to write an event handler:
CrossConnectivity.Current.ConnectivityChanged += (sender, args) =>
{
//your implementation
this.DisplayAlert("Connectivity Changed", "IsConnected: " + args.IsConnected.ToString(), "OK");
};
I have the solution for Android but i haven't started working on ios part
(better than nothing ;)
First Create a broadcastReceiver
public class Localize: BroadcastReceiver
{
public static Context context;
public Localize(Context ctx)
{
context = ctx;
}
public override void OnReceive (Context context, Intent intent)
{
isNetworkAvailable (context);
}
public void isNetworkAvailable(Context context)
{
Boolean state = false;
ConnectivityManager connectivity = (ConnectivityManager)
context.GetSystemService(Context.ConnectivityService);
if (connectivity != null)
{
NetworkInfo[] info = connectivity.GetAllNetworkInfo();
foreach (NetworkInfo nwork in info)
{
if (nwork.GetState () == NetworkInfo.State.Connected) {
ConnectionDetected();//Execute your fonction here
break;
}
}
}
}
Then register your broadcastreceiver with intent in your activity (in MainActivity for example)
IntentFilter filter = new IntentFilter(ConnectivityManager.ConnectivityAction);
receiver = new Localize(this);
RegisterReceiver(receiver, filter);
This should work as long as your application is running.. If you want a service that runs even if your App is killed you should create a service and then register broadcastReceiver in your service..
CrossConnectivity.Current.IsReachable("localhost");
this also works if you download package. I haven't tested it thoroughly

Resources