Search code examples
c#azurebotframework

Cannot post activity. Unauthorize. Bot emulator error


I have added these line of codes to my bot's Startup.cs in order for it to work in Azure's WebChat:

var appid = Configuration.GetSection("microsoftappid").Value;
var apppassword = Configuration.GetSection("microsoftapppassword").Value;
options.CredentialProvider = new SimpleCredentialProvider(appid, apppassword);

However, when I have these lines my bot does not work in Bot emulator. How can I integrate it with my bot so that I don't need to comment/uncomment the line of codes when publishing and opening my bot in the emulator. I am not that good yet in reading code so sorry for asking this. Thanks!

public class Startup
{
    private ILoggerFactory _loggerFactory;
    private bool _isProduction = false;

    public Startup(IHostingEnvironment env)
    {
        _isProduction = env.IsProduction();

        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        var secretKey = Configuration.GetSection("botFileSecret")?.Value;
        var botFilePath = Configuration.GetSection("botFilePath")?.Value;
        if (!File.Exists(botFilePath))
        {
            throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
        }

        BotConfiguration botConfig = null;
        try
        {
            botConfig = BotConfiguration.Load(botFilePath, secretKey);
        }
        catch
        {
            var msg = @"Error reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.
            - You can find the botFilePath and botFileSecret in the Azure App Service application settings.
            - If you are running this bot locally, consider adding a appsettings.json file with botFilePath and botFileSecret.
            - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.
            ";
            throw new InvalidOperationException(msg);
        }

        services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));

        var connectedServices = InitBotServices(botConfig);
        services.AddSingleton(sp => connectedServices);

        var environment = _isProduction ? "production" : "development";
        var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
        if (service == null && _isProduction)
        {

            service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == "development").FirstOrDefault();
        }

        if (!(service is EndpointService endpointService))
        {
            throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
        }

        services.AddBot<CoreBot>(options =>
        {
            options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
            options.ChannelProvider = new ConfigurationChannelProvider(Configuration);

            var appid = Configuration.GetSection("microsoftappid").Value;
            var apppassword = Configuration.GetSection("microsoftapppassword").Value;
            options.CredentialProvider = new SimpleCredentialProvider(appid, apppassword);

            ILogger logger = _loggerFactory.CreateLogger<CoreBot>();
            options.OnTurnError = async (context, exception) =>
            {
                logger.LogError($"Exception caught : {exception}");
                await context.SendActivityAsync($"Oops Sorry, it looks like something went wrong.");
            };

            IStorage dataStore = new MemoryStorage();

            var conversationState = new ConversationState(dataStore);
            options.State.Add(conversationState);

            var userState = new UserState(dataStore);
            options.State.Add(userState);

        });

        services.AddSingleton<BasicAccessors>(sp =>
        {
            var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
            if (options == null)
            {
                throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the state accessors");
            }

            var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
            if (conversationState == null)
            {
                throw new InvalidOperationException(
                    "ConversationState must be defined and added before adding conversation-scoped state accessors.");
            }

            var userState = options.State.OfType<UserState>().FirstOrDefault();
            if (userState == null)
            {
                throw new InvalidOperationException(
                    "UserState must be defined and added before adding user-scoped state accessors.");
            }

            var accessors = new BasicAccessors(conversationState, userState)
            {
                DialogStateAccessor = conversationState.CreateProperty<DialogState>(BasicAccessors.DialogStateAccessorName),
                UserStateAccessor = userState.CreateProperty<UserProfile>(BasicAccessors.UserStateAccessorName),
                ComplaintTicketAccessor = conversationState.CreateProperty<ComplaintTicket>(BasicAccessors.ComplaintTicketName),
            };

            return accessors;
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;

        app.UseDefaultFiles()
            .UseStaticFiles()
            .UseBotFramework();
    }

    private static BotServices InitBotServices(BotConfiguration config)
    {
        var qnaServices = new Dictionary<string, QnAMaker>();
        var luisServices = new Dictionary<string, LuisRecognizer>();

        foreach (var service in config.Services)
        {
            switch (service.Type)
            {
                case ServiceTypes.Luis:
                    {
                        if (!(service is LuisService luis))
                        {
                            throw new InvalidOperationException("The LUIS service is not configured correctly in your '.bot' file.");
                        }

                        var app = new LuisApplication(luis.AppId, luis.SubscriptionKey, luis.GetEndpoint());
                        var recognizer = new LuisRecognizer(app);
                        luisServices.Add(luis.Name, recognizer);
                        break;
                    }

                case ServiceTypes.Dispatch:

                    if (!(service is DispatchService dispatch))
                    {
                        throw new InvalidOperationException("The Dispatch service is not configured correctly in your '.bot' file.");
                    }        

                    var dispatchApp = new LuisApplication(dispatch.AppId, dispatch.AuthoringKey, dispatch.GetEndpoint());

                    var dispatchARecognizer = new LuisRecognizer(dispatchApp);
                    luisServices.Add(dispatch.Name, dispatchARecognizer);
                    break;

                case ServiceTypes.QnA:
                    {

                        if (!(service is QnAMakerService qna))
                        {
                            throw new InvalidOperationException("The QnA service is not configured correctly in your '.bot' file.");
                        }

                        var qnaEndpoint = new QnAMakerEndpoint()
                        {
                            KnowledgeBaseId = qna.KbId,
                            EndpointKey = qna.EndpointKey,
                            Host = qna.Hostname,
                        };

                        var qnaMaker = new QnAMaker(qnaEndpoint);
                        qnaServices.Add(qna.Name, qnaMaker);

                        break;
                    }
            }
        }

        return new BotServices(qnaServices, luisServices);
    }
}

}


Solution

  • You can use this code to meet your needs. If you run the code in your local environment, the value of _isProduction is false. If you run the code in Azure, the value of _isProduction is true.

           var appid = Configuration.GetSection("microsoftappid").Value;
           var apppassword = Configuration.GetSection("microsoftapppassword").Value;
           if (_isProduction)
           {
               options.CredentialProvider = new SimpleCredentialProvider(appid, apppassword);
           }
           else
           {
               options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
           }
    

    Add botFileSecret and botFilePath which were in your local bot appsettings.json file to the new appsettings.json file.

    enter image description here

    Now the code will be both working on azure and the emulator. Besides, I noticed that you are using Memory Storage which is for local bot debugging only. When the bot is restarted, everything stored in memory will be gone.

    IStorage dataStore = new MemoryStorage();
    

    For production bots, you can use the Azure Blob or Azure CosmosDB storage providers.