我创建了 Azure EchoBot 的最小设置。我的解决方案包括 ASP.NET Core 8 Web API 和 Angular 应用程序。我遵循 EchoBot 模板并将其部署到 Azure 应用服务
/linux/
。
Angular 应用程序安装了 WebChat 控件,我可以发送消息,这些消息显示在聊天中。但是,机器人在首次打开时不会返回任何答案或发送问候语。
这是 Chrome 中开发者控制台的屏幕截图:
这是我的 EchoBot 课程:
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);
}
}
}
}
}
这是
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": ""
}
在 Angular 应用程序中,我有以下内容
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);
}
});
}
}
我希望 EchoBot 回复“Echo:”+我在聊天窗口中输入的任何内容。
任何帮助将不胜感激!
浏览器控制台:(
)Uncaught (in promise) Error: A listener indicated an asynchronous response...
机器人的端点
/api/messages
可能无法正确响应或连接过早关闭,这是导致上述错误的唯一原因。
通过向
发出
POST
请求来测试端点 URL
https://limbo-dating-server-app-service.azurewebsites.net/api/messages
JSON 正文:
{
"type": "message",
"from": {
"id": "test-user"
},
"text": "Hello, bot!"
}
设置标题:
Authorization
:Bearer <DirectLineToken>
Content-Type
:application/json
。在 Azure Bot Service 中,确认
Messaging endpoint
就像
更新自定义适配器 (
AdapterWithErrorHandler
) 以记录有关错误的更多详细信息。
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$
可观察对象以确认是否从机器人收到活动:directLine.activity$.subscribe(activity => {
console.log('Activity received from bot:', activity);
});
已将应用程序部署到应用程序服务。
已测试:
在运行应用程序之前,使用 postman 或 curl 测试端点 url -
/api/messages
。