I created a minimal setup of Azure EchoBot. My solution includes an ASP.NET Core 8 Web API and an Angular app. I followed the template for the EchoBot and deployed it to an Azure app service /linux/
.
The Angular app has the WebChat control installed and I am able to send messages, which show up in the chat. However the bot is not returning any answers or sending a greeting when first opened.
Here is a screenshot of the developer console in Chrome:
Here is my EchoBot class:
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
namespace limbo.dating.Server.Bots
{
public class EchoBot : ActivityHandler
{
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
// Log the activity type
Console.WriteLine($"Activity Type: {turnContext.Activity.Type}");
await base.OnTurnAsync(turnContext, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var replyText = $"Echo: {turnContext.Activity.Text}";
await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var welcomeText = "Hello and welcome!";
// Log the number of members added
Console.WriteLine($"Members added count: {membersAdded.Count}");
foreach (var member in membersAdded)
{
// Log each member added
Console.WriteLine($"Member added: {member.Id}");
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
}
Here is the configuration in Program.cs
:
using limbo.dating.Server.Bots;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "AllowedCorsOrigins",
builder =>
{
builder.AllowAnyHeader()
.AllowAnyOrigin()
.AllowAnyMethod();
//.AllowCredentials();
});
});
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient();
// Create the Bot Framework Authentication to be used with the Bot Adapter.
builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// Create the Bot Adapter with error handling enabled.
builder.Services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
builder.Services.AddTransient<IBot, limbo.dating.Server.Bots.EchoBot>();
var app = builder.Build();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseWebSockets();
app.UseRouting();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapFallbackToFile("/index.html");
app.Run();
Appsettings.json
:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<mydomain>.onmicrosoft.com",
"TenantId": "<mytenantid>",
"ClientId": "<myclientid>",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"MicrosoftAppType": "MultiTenant",
"MicrosoftAppId": "<mymicrosoftappid>",
"MicrosoftAppPassword": "mymicrosoftapppassword",
"MicrosoftAppTenantId": ""
}
In the Angular app, I have the following for app.component.ts
:
import { HttpClient } from '@angular/common/http';
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import * as BotChat from "botframework-webchat";
import { createDirectLine } from 'botframework-webchat';
import { TokenService } from './services/token.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] // Corrected property name
})
export class AppComponent implements AfterViewInit, OnInit {
@ViewChild('botWindow')
botWindowElement!: ElementRef;
token!: string;
constructor(private tokenService: TokenService) { }
ngOnInit(): void {
// Fetch the token when the component initializes
this.tokenService.getDirectLineToken().subscribe(
(data) => {
this.token = data.token; // Assign the token
console.log('Token fetched:', this.token);
this.initializeWebChat(); // Initialize WebChat after token is fetched
},
(error) => {
console.error('Error fetching token:', error);
}
);
}
ngAfterViewInit(): void {
// Initialization moved to initializeWebChat method
}
initializeWebChat(): void {
const directLine = createDirectLine({
token: this.token
});
interface Action {
type: string;
payload?: any;
}
interface StoreAPI {
dispatch: (action: Action) => void;
}
const store = BotChat.createStore({},
(storeAPI: StoreAPI) => (next: (action: Action) => void) => (action: Action) => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
storeAPI.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: { language: window.navigator.language }
}
});
}
return next(action);
});
BotChat.renderWebChat({
directLine: directLine,
store: store,
userID: '[email protected]',
botID: 'limbo-dating-bot',
withCredential: true,
}, this.botWindowElement.nativeElement);
directLine.connectionStatus$.subscribe(status => {
if (status === 2) {
console.log('DirectLine connected');
} else if (status === 4) {
console.error('DirectLine connection failed');
}
});
directLine.connectionStatus$.subscribe(status => {
if (status === 2) {
console.log('DirectLine connected');
// Send a test message to the bot
directLine.postActivity({
from: { id: '[email protected]', name: 'User' },
type: 'message',
text: 'Hello, bot!'
}).subscribe(
id => console.log('Message sent, activity id:', id),
error => console.error('Error sending message:', error)
);
} else if (status === 4) {
console.error('DirectLine connection failed');
}
});
directLine.activity$.subscribe(activity => {
if (activity.from.id !== '[email protected]') {
console.log('Incoming message:', activity);
} else {
console.log('Outgoing message:', activity);
}
});
}
}
I expect the EchoBot to reply with "Echo: " + whatever I typed in the chat window.
Any help will be greatly appreciated!
browser console: (
Uncaught (in promise) Error: A listener indicated an asynchronous response...
)
The bot's endpoint /api/messages
might not be responding correctly or the connection is being closed prematurely this is the only cause for the above error.
Test the endpoint URL by making a POST
request to
https://limbo-dating-server-app-service.azurewebsites.net/api/messages
JSON body:
{
"type": "message",
"from": {
"id": "test-user"
},
"text": "Hello, bot!"
}
Set the headers:
Authorization
: Bearer <DirectLineToken>
Content-Type
: application/json
.In the Azure Bot Service, confirm the Messaging endpoint
is like
Update the custom adapter (AdapterWithErrorHandler
) to log more details about errors.
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger)
: base(configuration, logger)
{
OnTurnError = async (turnContext, exception) =>
{
logger.LogError(exception, "Unhandled error: {Message}", exception.Message);
// Send error message to user
await turnContext.SendActivityAsync("Sorry, something went wrong.");
};
}
}
activity$
observable to confirm if activities are received from the bot:directLine.activity$.subscribe(activity => {
console.log('Activity received from bot:', activity);
});
Deployed the application to app service.
Tested:
Before running the application test the endpoint url - /api/messages
using postman or curl.