Die Bot-Framework-Vorlagen und Beispiele werden für ASP.NET (C#), restify (JavaScript) und aiohttp (Python) geschrieben.
Die Webdienst-Features sind jedoch nicht Teil des Bot Framework SDK, sondern Teil des Webframeworks, das Sie verwenden möchten.
Alle Bot-Anwendungen teilen einige gemeinsame Features.
Funktion
Beschreibung
Ressourcenbereitstellung
Der Bot als Web-App muss einen Webdienst, einen Botadapter und ein Botobjekt erstellen.
Endpunkt Messaging
Die Web-App muss einen Messaging-Endpunkt implementieren, auf dem Aktivitäten empfangen und Aktivitäten an den Botadapter weitergeleitet werden sollen.
Bot-Adapter
Der Adapter empfängt Aktivitäten vom Messaging-Endpunkt, leitet sie an den Turn-Handler des Bots weiter und fängt Fehler oder Ausnahmen ab, die die Logik des Bots nicht abfangen kann.
Bot-Objekt
Das Bot-Objekt behandelt die Argumentation oder Logik des Bots für den Turn.
Die C#- und JavaScript-Vorlagen verfügen über integrierte Unterstützung für Streaming-Verbindungen. In diesem Artikel werden jedoch keine Streaming-Features behandelt. Informationen zu Streaming-Verbindungen finden Sie unter Verbinden eines Bots mit Direct Line Speech.
Hinweis
Die JavaScript-, C#- und Python-SDKs für Bot Framework werden weiterhin unterstützt, das Java-SDK wird jedoch eingestellt und der langfristige Support endet im November 2023.
Bestehende Bots, die mit dem Java SDK erstellt wurden, werden weiterhin funktionieren.
Das Bot Framework enthält sowohl VSIX- als auch .NET-Vorlagen.
Die Vorlage generiert eine in ASP.NET MVC Core erstellte Web-App. Wenn Sie sich die ASP.NET-Grundlagen ansehen, werden Sie ähnlichen Code in Dateien wie Program.cs und Startup.cs sehen. Diese Dateien sind für alle Web-Apps erforderlich und nicht botspezifisch.
Hinweis
Sie können die Vorlagen aus Visual Studio installieren.
Wählen Sie im Menü Erweiterungen die Option Erweiterungen verwalten aus.
Suchen Sie im Dialogfeld Erweiterungen verwalten nach Bot Framework v4 SDK-Vorlagen für Visual Studio und installieren Sie sie.
Die Datei appsettings.json gibt die Konfigurationsinformationen für Ihren Bot an, beispielsweise die App-ID, das Kennwort und Ähnliches. Wenn Sie bestimmte Technologien verwenden oder diesen Bot in einer Produktionsumgebung einsetzen, müssen Sie der Konfiguration Ihre spezifischen Schlüssel oder Ihre URL hinzufügen. Für diesen Echo-Bot müssen Sie an dieser Stelle jedoch keinerlei Schritte ausführen, d. h. die App-ID und das Passwort müssen hier nicht angegeben werden.
Die Datei EchoBot.csproj gibt Abhängigkeiten und die zugehörigen Versionen für Ihren Bot an. All dies wird durch die Vorlage und Ihr System eingerichtet. Zusätzliche Abhängigkeiten können mithilfe des NuGet-Paket-Managers oder des dotnet add package-Befehls installiert werden.
Der Yeoman-Generator erstellt eine Restify-Webanwendung. Wenn Sie sich in den zugehörigen Dokumenten den Schnellstart für Restify ansehen, werden Sie dort eine App finden, die der generierten Datei index.js ähnelt. Einige der wichtigsten Dateien, die von der Vorlage erzeugt werden, sind in diesem Artikel beschrieben. Der Code in einigen der Dateien wird zwar nicht kopiert, Sie sehen ihn jedoch, wenn Sie den Bot ausführen. Außerdem können Sie sich das Beispiel für den Node.js-Echobot ansehen.
Die package.json-Datei gibt Abhängigkeiten und die zugehörigen Versionen für Ihren Bot an. All dies wird durch die Vorlage und Ihr System eingerichtet. Zusätzliche Abhängigkeiten können mithilfe von npm install installiert werden.
Die ENV-Datei gibt die Konfigurationsinformationen für Ihren Bot an, beispielsweise die Portnummer, die App-ID, das Kennwort usw. Wenn Sie bestimmte Technologien verwenden oder diesen Bot in einer Produktionsumgebung einsetzen, müssen Sie der Konfiguration Ihre spezifischen Schlüssel oder Ihre URL hinzufügen. Für diesen Echobot müssen Sie an dieser Stelle jedoch keinerlei Schritte ausführen, d. h. die App-ID und das Kennwort müssen hier nicht angegeben werden.
Um die .env-Konfigurationsdatei zu verwenden, benötigt der Bot das dotenv-Paket von npm. Dies ist bereits als Abhängigkeit in der Datei package.json enthalten.
Der Yeoman-Generator erstellt eine Spring-basierte Webanwendung mit einer Builddatei mit Maven.
Die Maven pom.xml-Datei gibt Abhängigkeiten und die zugehörigen Versionen für Ihren Bot an. All dies wird durch die Vorlage und Ihr System eingerichtet. Zusätzliche Abhängigkeiten können durch Hinzufügen von Einträgen in der Datei pom.xml installiert werden.
Die application.properties-Datei gibt die Konfigurationsinformationen für Ihren Bot an, beispielsweise die Portnummer, die App-ID, das Kennwort usw. Wenn Sie bestimmte Technologien verwenden oder diesen Bot in einer Produktionsumgebung einsetzen, müssen Sie der Konfiguration Ihre spezifischen Schlüssel oder Ihre URL hinzufügen. Für diesen Echobot müssen Sie an dieser Stelle jedoch keinerlei Schritte ausführen, d. h. die App-ID und das Kennwort müssen hier nicht angegeben werden.
Die Python Cookiecutter-Vorlagen erstellen eine aiohttp-Webanwendung.
Die requirements.txt-Datei gibt Abhängigkeiten und die zugehörigen Versionen für Ihren Bot an. All dies wird durch die Vorlage und Ihr System eingerichtet. Zusätzliche Abhängigkeiten können mithilfe von pip install -r requirements.txt installiert werden
Die Datei config.py gibt die Konfigurationsinformationen für den Bot an, beispielsweise die Portnummer, die App-ID, das Kennwort usw. Wenn Sie bestimmte Technologien verwenden oder diesen Bot in einer Produktionsumgebung einsetzen, müssen Sie der Konfiguration Ihre spezifischen Schlüssel oder Ihre URL hinzufügen. Für diesen Echobot müssen Sie an dieser Stelle jedoch keinerlei Schritte ausführen, d. h. die App-ID und das Kennwort müssen hier nicht angegeben werden.
Ressourcenbereitstellung
Um als Web-App zu funktionieren, muss Ihr Bot einen Webdienst, einen Botadapter und ein Botobjekt erstellen.
Für die meisten Bots würden Sie auch Speicherschicht- und Speicherverwaltungsobjekte für den Bot erstellen.
Der Echo-Bot muss jedoch den Zustand zwischen Turns nicht beibehalten.
Und für einige Bots müssen Sie möglicherweise andere Objekte erstellen, die vom Bot-Objekt oder Adapter benötigt werden.
In ASP.NET registrieren Sie Objekte und Objekterstellungsmethoden in der Datei Startup.cs.
Die ConfigureServices-Methode lädt die verbundenen Dienste und deren Schlüssel (sofern vorhanden) aus appsettings.json, verbindet den Zustand usw. Hier werden der Adapter und der Bot definiert, der über die Abhängigkeitsinjektion verfügbar sein soll.
Anschließend beendet die Configure-Methode die Konfiguration Ihrer App.
ConfigureServices und Configure werden von der Runtime aufgerufen, wenn die App gestartet wird.
Im restify richten Sie den Webdienst und die Objekte ein, die er in der Datei index.js benötigt. Der Dienst, der Adapter und der Bot werden in den folgenden Abschnitten separat behandelt.
Im Spring richten Sie den Webdienst und die dafür benötigten Objekte in der Datei application.java ein. Die application.java enthält Kommentare, die die verschiedenen Komponenten und Framework-Klassen kennzeichnen, die von der Bot-Anwendung verwendet werden. Der Dienst, der Adapter und der Bot werden in den folgenden Abschnitten separat behandelt.
Im aiohttp richten Sie den Webdienst und die dafür benötigten Objekte in der Datei app.py ein. Der Dienst, der Adapter und der Bot werden in den folgenden Abschnitten separat behandelt.
Endpunkt Messaging
Die Vorlage implementiert einen Webdienst mit einem Messaging-Endpunkt.
Wenn er eine Anforderung empfängt, extrahiert der Dienst den Authentifizierungsheader und fordert die Nutzlast an den Adapter weiter.
Die C#- und JavaScript-SDKs unterstützen Streaming-Verbindungen. Während der Echo-Bot keines der Streaming-Features verwendet, ist der Adapter in den C#- und JavaScript-Vorlagen darauf ausgelegt, sie zu unterstützen.
Jede eingehende Anforderung stellt den Beginn eines neuen Turns dar.
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}
[HttpPost, HttpGet]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
index.js
const { EchoBot } = require('./bot');
// Create HTTP server
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
// Create an adapter scoped to this WebSocket connection to allow storing session data.
const streamingAdapter = new CloudAdapter(botFrameworkAuthentication);
// Set onTurnError for the CloudAdapter created for each connection.
streamingAdapter.onTurnError = onTurnErrorHandler;
Application.java
@Import({BotController.class})
app.py
# Main bot message handler.
if "application/json" in req.headers["Content-Type"]:
body = await req.json()
else:
return Response(status=HTTPStatus.UNSUPPORTED_MEDIA_TYPE)
activity = Activity().deserialize(body)
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
response = await ADAPTER.process_activity(auth_header, activity, BOT.on_turn)
if response:
return json_response(data=response.body, status=response.status)
return Response(status=HTTPStatus.OK)
APP = web.Application(middlewares=[aiohttp_error_middleware])
APP.router.add_post("/api/messages", messages)
if __name__ == "__main__":
try:
web.run_app(APP, host="localhost", port=CONFIG.PORT)
except Exception as error:
raise error
Der Botadapter
Der Adapter empfängt Aktivitäten vom Messaging-Endpunkt, leitet sie an den Turn-Handler des Bots weiter und fängt Fehler oder Ausnahmen ab, die die Logik des Bots nicht abfangen kann. Der Adapter leitet auch Aktivitäten von Ihrem Bot an den Kanal des Benutzers weiter.
Mit dem Adapter können Sie einen eigenen On-Turn-Error-Handler hinzufügen.
Der zu verwendende Adapter wird in der ConfigureServices-Methode definiert.
// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
AdapterWithErrorHandler.cs
public class AdapterWithErrorHandler : CloudAdapter
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger)
: base(auth, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
// NOTE: In production environment, you should consider logging this to
// Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
// to add telemetry capture to your bot.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
await turnContext.SendActivityAsync("The bot encountered an error or bug.");
await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code.");
// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
index.js
// application insights. See https://aka.ms/bottelemetry for telemetry
// configuration instructions.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};
// Set the onTurnError for the singleton CloudAdapter.
adapter.onTurnError = onTurnErrorHandler;
// Create the main dialog.
const myBot = new EchoBot();
// Listen for incoming requests.
server.post('/api/messages', async (req, res) => {
// Route received a request to adapter for processing
await adapter.process(req, res, (context) => myBot.run(context));
Application.java
Der zu verwendende Adapter wird in der getBotFrameworkAdapter-Methode definiert.
/**
* Returns a custom Adapter that provides error handling.
*
* @param configuration The Configuration object to use.
* @return An error handling BotFrameworkHttpAdapter.
*/
@Override
public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) {
return new AdapterWithErrorHandler(configuration);
}
Der AdapterWithErrorHandler wird im Java-SDK-Code im Paket com.microsoft.bot.integration definiert. Diese Klasse kann im Quellcode für das Java SDK überprüft werden.
app.py
from botbuilder.core import (
TurnContext,
)
from botbuilder.core.integration import aiohttp_error_middleware
from botbuilder.integration.aiohttp import CloudAdapter, ConfigurationBotFrameworkAuthentication
# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
ADAPTER = CloudAdapter(ConfigurationBotFrameworkAuthentication(CONFIG))
# Catch-all for errors.
# This check writes out errors to console log .vs. app insights.
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code."
)
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)
ADAPTER.on_turn_error = on_error
# Create the Bot
BOT = EchoBot()
Die Bot-Logik
Der Echo-Bot verwendet einen Aktivitätshandler und implementiert Handler für die Aktivitätstypen, die er erkennt und auf die er reagiert, in diesem Fall die Unterhaltungsaktualisierung und Nachrichten-Aktivitäten.
Eine Unterhaltungsaktualisierungs-Aktivität enthält Informationen darüber, wer der Unterhaltung beigetreten ist oder die Unterhaltung verlassen hat. Bei Nicht-Gruppen-Chats treten sowohl der Bot als auch der Benutzer der Unterhaltung bei, wenn sie gestartet wird. Bei Gruppen-Chats wird eine Unterhaltungsaktualisierung generiert, wenn jemand der Unterhaltung beitritt oder die Unterhaltung verlässt, unabhängig davon, ob es sich um den Bot oder einen Benutzer handelt.
Eine Nachrichtenaktivität stellt eine Nachricht dar, die der Benutzer an den Bot sendet.
Der Echo-Bot begrüßt einen Benutzer, wenn er an der Unterhaltung teilnimmt, und gibt alle Nachrichten zurück, die er an den Bot sendet.
Der zu verwendende Bot wird in der ConfigureServices-Methode definiert.
// Create the Bot Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
Bots\EchoBot.cs
public class EchoBot : ActivityHandler
{
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!";
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
index.js
const { EchoBot } = require('./bot');
});
// Listen for Upgrade requests for Streaming.
bot.js
const { ActivityHandler, MessageFactory } = require('botbuilder');
class EchoBot extends ActivityHandler {
constructor() {
super();
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
this.onMessage(async (context, next) => {
const replyText = `Echo: ${ context.activity.text }`;
await context.sendActivity(MessageFactory.text(replyText, replyText));
// By calling next() you ensure that the next BotHandler is run.
await next();
});
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
const welcomeText = 'Hello and welcome!';
for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
await context.sendActivity(MessageFactory.text(welcomeText, welcomeText));
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
}
Application.java
Der zu verwendende Bot wird in der getBot-Methode definiert.
/**
* Returns the Bot for this application.
*
* <p>
* The @Component annotation could be used on the Bot class instead of this method
* with the @Bean annotation.
* </p>
*
* @return The Bot implementation for this application.
*/
@Bean
public Bot getBot() {
return new EchoBot();
}
from botbuilder.core import (
TurnContext,
)
from botbuilder.core.integration import aiohttp_error_middleware
from botbuilder.integration.aiohttp import CloudAdapter, ConfigurationBotFrameworkAuthentication
bots/bot.py
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
from botbuilder.schema import ChannelAccount
class EchoBot(ActivityHandler):
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity("Hello and welcome!")
async def on_message_activity(self, turn_context: TurnContext):
return await turn_context.send_activity(
MessageFactory.text(f"Echo: {turn_context.activity.text}")
)