get access to Adapter and Configuration for IBot - botframework

I am using Twitter Adapter Sample.
In class TwitterAdapterSampleBot:IBot
I want to get access to IBotFrameworkHttpAdapter adapter, IConfiguration configuration and ILogger logger, which are created in Startup->ConfigureServices method
I tried simple implement constructor :
public class TwitterAdapterSampleBot : IBot
{
public TwitterAdapterSampleBot(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
But got internal exception on startup:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Bot.Builder.Integration.AspNet.Core.IBotFrameworkHttpAdapter' while attempting to activate 'TwitterAdapter_Sample.TwitterAdapterSampleBot'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChai

This "IBotFrameworkHttpAdapter" interface using to express the relationship between an mvc api Controller and a Bot Builder Adapter. So you need to resolve the dependency issue with it's implementation.
IBotFrameworkHttpAdapter is implemented in "BotFrameworkHttpAdapter" ( Bot Builder Adapter implementation ) class.
ConfigureServices in Asp.Net Core Startup Class:
services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
You can implement the above scenario in another way for example create a botframework custom adapter error handler class with the implementation of BotFrameworkHttpAdapter.
Microsoft docs example:
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
private static log4net.ILog logger
= log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public AdapterWithErrorHandler(
ICredentialProvider credentialProvider,
ConversationState conversationState = null)
: base(credentialProvider)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.Error($"Exception caught : {exception.Message}");
// Send a catch-all apology to the user.
await turnContext.SendActivityAsync("Sorry, it looks like something went wrong.");
if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.Error($"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
};
}
}
ConfigureServices in Asp.Net Core Startup Class:
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
Reference:
Microsoft V4 docs
BotFrameworkHttpAdapter docs

Related

How to change locale for a conversation in runtme

I'm using botframework composer with multi language and want each user to be able to select preferred language/locale. After resolving the local code for his selection with a choice dialog, how can I set it in conversation so that his locale setting in his device will be overruled for rest of conversation?
Changing locale in emulator works fine, want same behaviour after user selection.
Setting turn.locale works for one turn, but is reset on next turn.
supposing you don't have control over the client, which would be the best.
You can resort to an old overload on the ever-growing hierarchy of bot adapters that hasn't been marked as deprecated.
You'd have to use the PostAsync method (api/post-messages endpoint) in the following controller (showing the one created by the current set of bot framework templates just for comparison):
[Route("api")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter StreamingAdapter;
private readonly BotFrameworkAdapter PostAdapter;
private readonly ConversationLocales ConversationLocales;
private readonly IBot Bot;
public BotController(
IBotFrameworkHttpAdapter streamingAdapter,
BotFrameworkAdapter postAdapter,
ConversationLocales conversationLocales,
IBot bot)
{
StreamingAdapter = streamingAdapter;
PostAdapter = postAdapter;
Bot = bot;
}
[HttpPost("messages"), HttpGet("messages")]
public async Task PostOrStreamingAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await StreamingAdapter.ProcessAsync(Request, Response, Bot);
}
[HttpPost("post-messages")]
public async Task<InvokeResponse> PostAsync([FromBody] Activity activity)
{
var savedLocale = ConversationLocales.GetLocaleForConversation(activity.Conversation.Id);
activity.Locale = savedLocale ?? activity.Locale;
return await PostAdapter.ProcessActivityAsync(string.Empty, activity, Bot.OnTurnAsync, default);
}
}
That's supposing you implement a ConversationLocales service that allows you to keep the selected locale for each conversation id.
In the code above we're using the BotFrameworkAdapter adapter instead of IBotFrameworkHttpAdapter, however the AdapterWithErrorHandler used in the templates inherits indirectly from BotFrameworkAdapter, so you could do something like this in ConfigureServices to register "both" adapters:
services.AddSingleton<AdapterWithErrorHandler>();
services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
services.AddSingleton<BotFrameworkAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
To have a single adapter instance.
Using this method the adapter won't be able to use the bot channel streaming endpoints, but that shouldn't be much of a trouble, as long as you don't use the speech client.
You can also read some other details that might be relevan to you in my blog post How does a Bot Builder v4 bot work?, it's a bit dated but still valid.
UPDATE - Found a better solution 😊
This one works with the current wave of adapters and uses the messages pipeline, so it's "modern".
It also requires you to use a custom runtime, that you'll customize as follows.
1 - Create the following middleware
public class LocaleSelectionMiddleware : IMiddleware
{
private readonly IStatePropertyAccessor<string> _userLocale;
public LocaleSelectionMiddleware(UserState userState)
{
_userLocale = userState.CreateProperty<string>("locale");
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
if (turnContext is null)
{
throw new ArgumentNullException(nameof(turnContext));
}
var userLocale = await _userLocale.GetAsync(turnContext, () => turnContext.Activity.Locale);
turnContext.Activity.Locale = userLocale;
(turnContext as TurnContext).Locale = userLocale;
await next(cancellationToken).ConfigureAwait(false);
}
}
2 - Configure the middleware in the adapter in GetBotAdapter() in Startup.cs
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
this.HostingEnvironment = env;
this.Configuration = configuration;
}
//...
public BotFrameworkHttpAdapter GetBotAdapter(IStorage storage, BotSettings settings, UserState userState, ConversationState conversationState, IServiceProvider s)
{
var adapter = IsSkill(settings)
? new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration), s.GetService<AuthenticationConfiguration>())
: new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration));
adapter
.UseStorage(storage)
.UseBotState(userState, conversationState)
.Use(new RegisterClassMiddleware<IConfiguration>(Configuration))
.Use(new LocaleSelectionMiddleware(userState)) // <-- Add the middleware here
.Use(s.GetService<TelemetryInitializerMiddleware>());
//...
return adapter;
}
//...
}
3 - Set the user.locale property in any dialog
Set the user.locale property from any dialog, and the next turn will have the desired locale, and will be persisted in the user state, until they change it again.

ShowTypingMiddleware broke my bot when deployed - BotFramework V4

I'm trying to use the ShowTypingMiddleware, a custom middleware already given by BotFramework (see here: https://github.com/microsoft/botbuilder-dotnet/blob/master/libraries/Microsoft.Bot.Builder/ShowTypingMiddleware.cs) to send an typing message to my user while the bot is processing his request. I'm using the BotFramework V4.
It all works locally, but not when I publish it on Azure's WebChat.
I've followed the example in Microsoft's samples, where they create an adapter that adds the desired middleware to the bot pipeline (the sample I've used is here: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/17.multilingual-bot. The custom adapter I'm referring to is AdapterWithErrorHandler.cs, and it adds the TranslationMiddleware to the pipeline).
Running locally, everything works as planned. The problem is: when I'm publishing it to Azure, the webchat stop working. It throws the following exception:
7‎/‎21‎/‎2019‎ ‎1‎:‎07‎:‎33‎ ‎PM There was an error sending this message to your bot: HTTP status code Unauthorized
‎7‎/‎21‎/‎2019‎ ‎1‎:‎07‎:‎33‎ ‎PM There was an error sending this message to your bot:
HTTP status code Unauthorized
In my StartUp.cs's ConfigureServices, I've injected my custom adapter and my middleware:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the credential provider to be used with the Bot Framework Adapter.
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Create the Bot Framework Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithTypingAndErrorHandler>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, MyBot>();
services.AddSingleton<ShowTypingMiddleware>();
}
My AdapterWithTypingAndErrorHandler is as follows:
public class AdapterWithTypingAndErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithTypingAndErrorHandler(
IConfiguration configuration,
ILogger<BotFrameworkHttpAdapter> logger,
ShowTypingMiddleware showTypingMiddleware) : base(logger: logger)
{
if (showTypingMiddleware == null)
throw new NullReferenceException($"Could not load '{nameof(showTypingMiddleware)}' in custom adapter.");
AddAdapterToPipeline(showTypingMiddleware);
OnTurnError = async (turnContext, exception) =>
{
logger.LogError($"Exception caught : {exception.Message}");
await turnContext.SendActivityAsync("Sorry, something went wrong :/");
};
}
private void AddAdapterToPipeline(ShowTypingMiddleware showTypingMiddleware)
=> Use(showTypingMiddleware);
}
And I'm using it in my controller:
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter Adapter;
private readonly IBot Bot;
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
Adapter = adapter;
Bot = bot;
}
[HttpPost]
public async Task PostAsync()
{
await Adapter.ProcessAsync(Request, Response, Bot);
}
}
As I said, everything works fine, locally, but when I publish it, the WebChat throws the Unauthorized exception. If I use the default Adapter (the BotFrameworkHttpAdapter.cs one), instead of my customized, it all works fine too.
What should I do?

Issue with Simple Injector while using with Web API

I am having issue using Simple Injector with WebAPI project that gets created default with VS 2015.
I am having the AccountController having the below constructor
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
In order to register these I used the below code in Simple Injector
// Create the container.
var apiContainer = new Container();
apiContainer.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
apiContainer.Options.ConstructorResolutionBehavior = new ConstructorBehavior();
//register the classes that we are going to use for dependency injection
apiContainer.Register<IUserStore<ApplicationUser>>(() => new UserStore<ApplicationUser>(new ApplicationDbContext()),Lifestyle.Scoped);
apiContainer.Register<IDataProtector>(() => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider().Create("ASP.NET Identity"),Lifestyle.Transient);
apiContainer.Register<ISecureDataFormat<AuthenticationTicket>, SecureDataFormat<AuthenticationTicket>>(Lifestyle.Transient);
apiContainer.Register<ITextEncoder, Base64UrlTextEncoder>(Lifestyle.Scoped);
apiContainer.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>(Lifestyle.Scoped);
//apiContainer.RegisterCommonClasses();
//register the webapi controller
apiContainer.RegisterWebApiControllers(configuration);
but after this I am getting the warning message that says
[Disposable Transient Component] ApplicationUserManager is registered as transient, but implements IDisposable.
Can someone Please help me with this how to resolve this ? With Default Web api project with VS 2015 it adds Account controller and that use ApplicationUserManager and has below details
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
Another issue I am getting as below
The constructor of type HttpConfiguration contains the parameter with name 'routes' and type HttpRouteCollection that is not registered. Please ensure HttpRouteCollection is registered, or change the constructor of HttpConfiguration.
This is with the HelpController as it uses the below details:
public HelpController()
: this(GlobalConfiguration.Configuration)
{
}
public HelpController(HttpConfiguration config)
{
Configuration = config;
}

Getting Java.Lang.NullPointerException when trying to open GPS Settings page using Xamarin

I am getting the following error when trying to open GPS settings page if GPS is not enabled (within Xamarin):
Unknown identifier: StartActivity
Unhandled Exception:
Java.Lang.NullPointerException:
Can somebody please guide where am I getting wrong?
This My Interface
namespace MyApp
{
public interface GpsSettings
{
void showGpsSettings();
}
}
This the Implementation
[assembly: Xamarin.Forms.Dependency(typeof(GpsSettingsImplementation))]
namespace MyApp.Droid
{
public class GpsSettingsImplementation : Activity, GpsSettings
{
public GpsSettingsImplementation()
{
}
public void showGpsSettings()
{
var intent = new Intent(Android.Provider.Settings.ActionLocationSourceSettings);
StartActivity(intent);
}
}
}
This is how I call my function on button click
DependencyService.Get<GpsSettings>().showGpsSettings();
An existing Activity instance has a bit of work that goes on behind
the scenes when it's constructed; activities started through the
intent system (all activities) will have a Context reference added to
them when they are instantiated. This context reference is used in the
call-chain of StartActivity.
So, the Java.Lang.NullPointerException seen after invoking
StartActivity on your Test activity instance is because the Context
inside that instance has never been set. By using the new operator to
create an activity instance you've circumvented the normal way
activities are instantiated, leaving your instance in an invalid
state!
ref: https://stackoverflow.com/a/31330999/5145530
The above error can be resolved in the following manner:
[assembly: Xamarin.Forms.Dependency(typeof(GpsSettingsImplementation))]
namespace MyApp.Droid
{
public class GpsSettingsImplementation : Activity, GpsSettings
{
public GpsSettingsImplementation()
{
}
public void showGpsSettings()
{
var intent = new Intent(Android.Provider.Settings.ActionLocationSourceSettings);
intent.SetFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
}

Send DataBase table via observable collection through WCF service and consume it in windowws Phone app

I want to send an observablecollection object through a WCF service and receive it in windows phone 8 app.
the service Is as below.
public ObservableCollection<State> GetName()
{
StateEntities objEntities = new StateEntities();
ObservableCollection<State> stateCollection = new ObservableCollection<State>();
foreach (State s in objEntities.States)
{
stateCollection.Add(s);
}
return stateCollection;
}
State is a Class having data from the table. The contract is given below
public interface IHost
{
[OperationContract]
string DoWork();
[OperationContract]
ObservableCollection<State> GetName();
}
Now I want to consume the service in a windows phone app.
A button click should trigger the service consumption , get data from the service as observable collection and feed it into a gridview.
public sealed partial class MainPage : Page
{
ServiceReference1.HostClient proxy;
public MainPage()
{
proxy = new HostClient();
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<State> stateCollection = new ObservableCollection<State>();
stateCollection = await proxy.GetNameAsync();
dataGrid.ItemsSource = stateCollection;
}
}
but it is throwing and exception in the await line.
This is the exception.
An exception of type 'System.ServiceModel.CommunicationException'
occurred in mscorlib.dll but was not handled in user code
Additional information: An error occurred while receiving the HTTP
response to "ttp://localhost:65338/Host.svc." This could be due to the
service endpoint binding not using the HTTP protocol. This could also
be due to an HTTP request context being aborted by the server
Can anybody suggest an alternative or solution ?

Resources