APPLIES TO: SDK v4
An activity handler is an event-driven way to organize the conversational logic for your bot.
Each different type or subtype of activity represents a different type of conversational event.
Under the covers, the bot's turn handler calls the individual activity handler for whatever type of activity it received.
For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the on message activity activity handler. When building your bot, your bot logic for handling and responding to messages will go in this on message activity handler. Likewise, your logic for handling members being added to the conversation will go in your on members added handler, which is called whenever a member is added to the conversation.
For other ways to organize your bot logic, see the bot logic section in how bots work.
Note
The Bot Framework JavaScript, C#, and Python SDKs will continue to be supported, however, the Java SDK is being retired with final long-term support ending in November 2023.
Existing bots built with the Java SDK will continue to function.
For new bot building, read about choosing the right copilot solution.
For more information, see The future of bot building.
To implement your logic for these handlers, you'll override these methods in your bot, such as in the sample activity handler section below. For each of these handlers, there's no base implementation, so just add the logic that you want in your override.
There are certain situations where you'll want to override the base turn handler, such as saving state at the end of a turn. When doing so, be sure to first call await base.OnTurnAsync(turnContext, cancellationToken);
to make sure the base implementation of OnTurnAsync
is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as OnMessageActivityAsync
.
The JavaScript ActivityHandler
uses an event emitter and listener pattern.
For example, use the onMessage
method to register an event listener for message activities. You can register more than one listener. When the bot receives a message activity, the activity handler would see that incoming activity and send it each of the onMessage
activity listeners, in the order in which they were registered.
When building your bot, your bot logic for handling and responding to messages will go in the onMessage
listeners. Likewise, your logic for handling members being added to the conversation will go in your onMembersAdded
listeners, which are called whenever a member is added to the conversation.
To add these listeners, you'll register them in your bot as seen in the Bot logic section below. For each listener, include your bot logic, then be sure to call next()
at the end. By calling next()
, you ensure that the next listener is run.
Make sure to save state before the turn ends. You can do so by overriding the activity handler run
method and saving state after the parent's run
method completes.
There are no common situations where you'll want to override the base turn handler, so be careful if you try to do so.
There's a special handler called onDialog
. The onDialog
handler runs at the end, after the rest of the handlers have run, and isn't tied to a certain activity type. As with all the above handlers, be sure to call next()
to ensure the rest of the process wraps up.
To implement your logic for these handlers, you'll override these methods in your bot, such as in the sample activity handler section below. There's no base implementation for each of these handlers, so add the logic you want in your override.
There are certain situations where you'll want to override the base turn handler, such as saving state at the end of a turn. When doing so, be sure first to call super.onTurn(turnContext);
to make sure the base implementation of onTurn
is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as onMessageActivity
.
When building your bot, your bot logic for handling and responding to messages will go in this on_message_activity
handler. Likewise, your logic for handling members being added to the conversation will go in your on_members_added
handler, which is called whenever a member is added to the conversation.
For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the on_message_activity
activity handler.
To implement your logic for these handlers, you'll override these methods in your bot, such as in the sample activity handler section below. For each of these handlers, there's no base implementation, so just add the logic that you want in your override.
There are certain situations where you'll want to override the base turn handler, such as saving state at the end of a turn. When doing so, be sure to first call await super().on_turn(turnContext);
to make sure the base implementation of on_turn
is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as on_message_activity
.
Activity handling
The bot logic processes incoming activities from one or more channels and generates outgoing activities in response.
The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from ActivityHandler
, which implements the IBot
interface. ActivityHandler
defines various handlers for different types of activities, such as OnMessageActivityAsync
, and OnMembersAddedAsync
. These methods are protected, but can be overridden, since we're deriving from ActivityHandler
.
The handlers defined in ActivityHandler
are:
Event |
Handler |
Description |
Any activity type received |
OnTurnAsync |
Calls one of the other handlers, based on the type of activity received. |
Message activity received |
OnMessageActivityAsync |
Override this to handle a message activity. |
Conversation update activity received |
OnConversationUpdateActivityAsync |
On a conversationUpdate activity, calls a handler if members other than the bot joined or left the conversation. |
Non-bot members joined the conversation |
OnMembersAddedAsync |
Override this to handle members joining a conversation. |
Non-bot members left the conversation |
OnMembersRemovedAsync |
Override this to handle members leaving a conversation. |
Event activity received |
OnEventActivityAsync |
On an event activity, calls a handler specific to the event type. |
Token-response event activity received |
OnTokenResponseEventAsync |
Override this to handle token response events. |
Non-token-response event activity received |
OnEventAsync |
Override this to handle other types of events. |
Message reaction activity received |
OnMessageReactionActivityAsync |
On a messageReaction activity, calls a handler if one or more reactions were added or removed from a message. |
Message reactions added to a message |
OnReactionsAddedAsync |
Override this to handle reactions added to a message. |
Message reactions removed from a message |
OnReactionsRemovedAsync |
Override this to handle reactions removed from a message. |
Installation update activity received |
OnInstallationUpdateActivityAsync |
On an installationUpdate activity, calls a handler based on whether the bot was installed or uninstalled. |
Bot installed |
OnInstallationUpdateAddAsync |
Override this to add logic for when the bot is installed within an organizational unit. |
Bot uninstalled |
OnInstallationUpdateRemoveAsync |
Override this to add logic for when the bot is uninstalled within an organizational unit. |
Other activity type received |
OnUnrecognizedActivityTypeAsync |
Override this to handle any activity type otherwise unhandled. |
These different handlers have a turnContext
that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, OnMessageActivityAsync
will always be handled, and is generally the most common.
As in previous 4.x versions of this framework, there's also the option to implement the public method OnTurnAsync
. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of OnTurnAsync
, it's still an option.
Important
If you do override the OnTurnAsync
method, you'll need to call base.OnTurnAsync
to get the base implementation to call all the other On<activity>Async
handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run.
The main bot logic is defined in the bot code. To implement a bot as an activity handler, extend ActivityHandler
. ActivityHandler
defines various events for different types of activities, and you can modify your bot's behavior by registering event listeners, such as with onMessage
and onConversationUpdate
.
Use these methods to register listeners for each type of event:
Event |
Registration method |
Description |
Any activity type received |
onTurn |
Registers a listener for when any activity is received. |
Message activity received |
onMessage |
Registers a listener for when a message activity is received. |
Conversation update activity received |
onConversationUpdate |
Registers a listener for when any conversationUpdate activity is received. |
Members joined the conversation |
onMembersAdded |
Registers a listener for when members joined the conversation, including the bot. |
Members left the conversation |
onMembersRemoved |
Registers a listener for when members left the conversation, including the bot. |
Message reaction activity received |
onMessageReaction |
Registers a listener for when any messageReaction activity is received. |
Message reactions added to a message |
onReactionsAdded |
Registers a listener for when reactions are added to a message. |
Message reactions removed from a message |
onReactionsRemoved |
Registers a listener for when reactions are removed from a message. |
Event activity received |
onEvent |
Registers a listener for when any event activity is received. |
Token-response event activity received |
onTokenResponseEvent |
Registers a listener for when a tokens/response event is received. |
Installation update activity received |
onInstallationUpdate |
Registers a listener for when any installationUpdate activity is received. |
Bot installed |
onInstallationUpdateAdd |
Registers a listener for when the bot is installed within an organizational unit. |
Bot uninstalled |
onInstallationUpdateRemove |
Registers a listener for when the bot is uninstalled within an organizational unit. |
Other activity type received |
onUnrecognizedActivityType |
Registers a listener for when a handler for the specific type of activity isn't defined. |
Activity handlers have completed |
onDialog |
Called after any applicable handlers have completed. |
Call the next
continuation function from each handler to allow processing to continue. If next
isn't called, processing of the activity ends.
The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from ActivityHandler
, which implements the Bot
interface. ActivityHandler
defines various handlers for different types of activities, such as onMessageActivity
, and onMembersAdded
. These methods are protected, but can be overridden, since we're deriving from ActivityHandler
.
The handlers defined in ActivityHandler
are:
Event |
Handler |
Description |
Any activity type received |
onTurn |
Calls one of the other handlers, based on the type of activity received. |
Message activity received |
onMessageActivity |
Override this to handle a message activity. |
Conversation update activity received |
onConversationUpdateActivity |
On a conversationUpdate activity, calls a handler if members other than the bot joined or left the conversation. |
Non-bot members joined the conversation |
onMembersAdded |
Override this to handle members joining a conversation. |
Non-bot members left the conversation |
onMembersRemoved |
Override this to handle members leaving a conversation. |
Event activity received |
onEventActivity |
On an event activity, calls a handler specific to the event type. |
Token-response event activity received |
onTokenResponseEvent |
Override this to handle token response events. |
Non-token-response event activity received |
onEvent |
Override this to handle other types of events. |
Message reaction activity received |
onMessageReactionActivity |
On a messageReaction activity, calls a handler if one or more reactions were added or removed from a message. |
Message reactions added to a message |
onReactionsAdded |
Override this to handle reactions added to a message. |
Message reactions removed from a message |
onReactionsRemoved |
Override this to handle reactions removed from a message. |
Installation update activity received |
onInstallationUpdate |
On an installationUpdate activity, calls a handler based on whether the bot was installed or uninstalled. |
Bot installed |
onInstallationUpdateAdd |
Override this to add logic for when the bot is installed within an organizational unit. |
Bot uninstalled |
onInstallationUpdateRemove |
Override this to add logic for when the bot is uninstalled within an organizational unit. |
Other activity type received |
onUnrecognizedActivityType |
Override this to handle any activity type otherwise unhandled. |
These different handlers have a turnContext
that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, onMessageActivity
will always be handled, and is generally the most common.
There's also the option to implement the public method onTurn
. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of onTurn
, it's still an option.
Important
If you do override the onTurn
method, you'll need to call super.onTurn
to get the base implementation to call all the other on<activity>
handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run.
The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from ActivityHandler
, which in turn derives from the abstract Bot
class. ActivityHandler
defines various handlers for different types of activities, such as on_message_activity
and on_members_added
. These methods are protected, but can be overridden, since we're deriving from ActivityHandler
.
The handlers defined in ActivityHandler
are:
Event |
Handler |
Description |
Any activity type received |
on_turn |
Calls one of the other handlers, based on the type of activity received. |
Message activity received |
on_message_activity |
Override this to handle a message activity. |
Conversation update activity received |
on_conversation_update_activity |
On a conversationUpdate activity, calls a handler if members other than the bot joined or left the conversation. |
Non-bot members joined the conversation |
on_members_added_activity |
Override this to handle members joining a conversation. |
Non-bot members left the conversation |
on_members_removed_activity |
Override this to handle members leaving a conversation. |
Event activity received |
on_event_activity |
On an event activity, calls a handler specific to the event type. |
Token-response event activity received |
on_token_response_event |
Override this to handle token response events. |
Non-token-response event activity received |
on_event_activity |
Override this to handle other types of events. |
Message reaction activity received |
on_message_reaction_activity |
On a messageReaction activity, calls a handler if one or more reactions were added or removed from a message. |
Message reactions added to a message |
on_reactions_added |
Override this to handle reactions added to a message. |
Message reactions removed from a message |
on_reactions_removed |
Override this to handle reactions removed from a message. |
Installation update activity received |
on_installation_update |
On an installationUpdate activity, calls a handler based on whether the bot was installed or uninstalled. |
Bot installed |
on_installation_update_add |
Override this to add logic for when the bot is installed within an organizational unit. |
Bot uninstalled |
on_installation_update_remove |
Override this to add logic for when the bot is uninstalled within an organizational unit. |
Other activity type received |
on_unrecognized_activity_type |
Override this to handle any activity type otherwise unhandled. |
These different handlers have a turn_context
that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, on_message_activity
will always be handled, and is generally the most common.
As in previous 4.x versions of this framework, there's also the option to implement the public method on_turn
. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of on_turn
, it's still an option.
Important
If you do override the on_turn
method, you'll need to call super().on_turn
to get the base implementation to call all the other on_<activity>
handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run.
Sample activity handler
For example, you can handle on members added to welcome users to a conversation, and handle on message to echo back messages they send to the bot.
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);
}
}
}
}
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();
});
}
}
public class EchoBot extends ActivityHandler {
@Override
protected CompletableFuture<Void> onMessageActivity(TurnContext turnContext) {
return turnContext.sendActivity(
MessageFactory.text("Echo: " + turnContext.getActivity().getText())
).thenApply(sendResult -> null);
}
@Override
protected CompletableFuture<Void> onMembersAdded(
List<ChannelAccount> membersAdded,
TurnContext turnContext
) {
String welcomeText = "Hello and welcome!";
return membersAdded.stream()
.filter(
member -> !StringUtils
.equals(member.getId(), turnContext.getActivity().getRecipient().getId())
).map(channel -> turnContext.sendActivity(MessageFactory.text(welcomeText, welcomeText, null)))
.collect(CompletableFutures.toFutureList()).thenApply(resourceResponses -> null);
}
}
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}")
)
Next steps
- The Microsoft Teams channel introduces some Teams-specific activities that your bot will need to support to work properly with Teams. To understand key concepts of developing bots for Microsoft Teams, see How Microsoft Teams bots work
- An activity handler is a good way to design a bot that doesn't need to track conversational state between turns. The dialogs library provides ways to manage a long-running conversation with the user.