When building your conversational agents for Microsoft Teams, you can work with conversation events. Teams sends notifications to your agent for conversation events that happen in scopes where your agent is active. You can capture these events in your code and take the following actions:
- Trigger a welcome message when your agent is added to a team.
- Trigger a welcome message when a new team member is added or removed.
- Trigger a notification when a channel is created, renamed, or deleted.
- Trigger a notification when an agent message is liked by a user.
- Identify the default channel for your agent from user input (selection) during installation.
Conversation update events
You can use conversation update events to provide better notifications and effective agent actions.
Important
- You can add new events anytime and your agent begins to receive them.
- Design your agent to receive unexpected events.
- If you are using the Teams SDK, your agent automatically responds with a
200 - OK to any events you choose not to handle.
- When an Azure Communication Services (ACS) client joins or leaves the Teams meeting, no conversation update events are triggered.
An agent receives a conversationUpdate event in either of the following cases:
- When the agent is added to a conversation.
- Other members are added to or removed from a conversation.
- Conversation metadata has changed.
The conversationUpdate event is sent to your agent when it receives information on membership updates for teams where it has been added. It also receives an update when it has been added for the first time for personal conversations.
The following table shows a list of Teams conversation update events with more details:
Channel created
The channelCreated event is sent to your agent whenever a new channel is created in a team where your agent is installed.
The following code shows an example of a channel created event:
app.OnChannelCreated(async context =>
{
var channelName = context.Activity.ChannelData.Channel.Name;
await context.Send($"{channelName} is the Channel created");
});
app.on('channelCreated', async ({ activity, send }) => {
const channelName = activity.channelData.channel.name;
await send(`${channelName} is the Channel created`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:34:07.478Z",
"localTimestamp": "2017-02-23T12:34:07.478-07:00",
"id": "f:dd6ec311",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1wR7IdIRIoerMIWbewMi75JA3scaMuxvFon9eRQW2Nix5loMDo0362st2IaRVRirPZBv1WdXT8TIFWWmlQCizZQ"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:6d97d816470f481dbcda38244b98689a@thread.skype",
"name": "FunDiscussions"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"eventType": "channelCreated",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_channel_created
async def handle_channel_created(ctx: ActivityContext[ConversationUpdateActivity]):
channel = ctx.activity.channel_data.channel
await ctx.send(f"The new channel is {channel.name}. The channel id is {channel.id}")
Channel renamed
The channelRenamed event is sent to your agent whenever a channel is renamed in a team where your agent is installed.
The following code shows an example of a channel renamed event:
app.OnChannelRenamed(async context =>
{
var channelName = context.Activity.ChannelData.Channel.Name;
await context.Send($"{channelName} is the new Channel name");
});
app.on('channelRenamed', async ({ activity, send }) => {
const channelName = activity.channelData.channel.name;
await send(`${channelName} is the new Channel name`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:34:07.478Z",
"localTimestamp": "2017-02-23T12:34:07.478-07:00",
"id": "f:dd6ec311",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1wR7IdIRIoerMIWbewMi75JA3scaMuxvFon9eRQW2Nix5loMDo0362st2IaRVRirPZBv1WdXT8TIFWWmlQCizZQ"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:6d97d816470f481dbcda38244b98689a@thread.skype",
"name": "PhotographyUpdates"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"eventType": "channelRenamed",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_channel_renamed
async def handle_channel_renamed(ctx: ActivityContext[ConversationUpdateActivity]):
channel_name = ctx.activity.channel_data.channel.name
await ctx.send(f"The new channel name is {channel_name}")
Channel deleted
The channelDeleted event is sent to your agent, whenever a channel is deleted in a team where your agent is installed.
The following code shows an example of a channel deleted event:
app.OnChannelDeleted(async context =>
{
var channelName = context.Activity.ChannelData.Channel.Name;
await context.Send($"{channelName} is the Channel deleted");
});
app.on('channelDeleted', async ({ activity, send }) => {
const channelName = activity.channelData.channel.name;
await send(`${channelName} is the Channel deleted`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:34:07.478Z",
"localTimestamp": "2017-02-23T12:34:07.478-07:00",
"id": "f:dd6ec311",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1wR7IdIRIoerMIWbewMi75JA3scaMuxvFon9eRQW2Nix5loMDo0362st2IaRVRirPZBv1WdXT8TIFWWmlQCizZQ"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:6d97d816470f481dbcda38244b98689a@thread.skype",
"name": "PhotographyUpdates"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"eventType": "channelDeleted",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_channel_deleted
async def handle_channel_deleted(ctx: ActivityContext[ConversationUpdateActivity]):
channel_name = ctx.activity.channel_data.channel.name
await ctx.send(f"The deleted channel is {channel_name}")
Channel restored
The channelRestored event is sent to your agent, whenever a channel that was previously deleted is restored in a team where your agent is already installed.
The following code shows an example of a channel restored event:
app.OnChannelRestored(async context =>
{
var channelName = context.Activity.ChannelData.Channel.Name;
await context.Send($"{channelName} is the Channel restored.");
});
app.on('channelRestored', async ({ activity, send }) => {
const channelName = activity.channelData.channel.name;
await send(`${channelName} is the Channel restored`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:34:07.478Z",
"localTimestamp": "2017-02-23T12:34:07.478-07:00",
"id": "f:dd6ec311",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1wR7IdIRIoerMIWbewMi75JA3scaMuxvFon9eRQW2Nix5loMDo0362st2IaRVRirPZBv1WdXT8TIFWWmlQCizZQ"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:6d97d816470f481dbcda38244b98689a@thread.skype",
"name": "FunDiscussions"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"eventType": "channelRestored",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_channel_restored
async def handle_channel_restored(ctx: ActivityContext[ConversationUpdateActivity]):
channel = ctx.activity.channel_data.channel
await ctx.send(
f"The restored channel is {channel.name}. The channel id is {channel.id}"
)
Members added
A member added event is sent to your agent in the following scenarios:
When the agent, itself, is installed and added to a conversation
In team context, the activity's conversation.id is set to the id of the channel selected by the user during app installation or the channel where the agent was installed.
When a user is added to a conversation where the agent is installed
User ids received in the event payload are unique to the agent and can be cached for future use, such as directly messaging a user.
The member added activity eventType is set to teamMemberAdded when the event is sent from a team context. To determine if the new member added was the agent itself or a user, check the Activity object of the Context object. If the MembersAdded list contains an object where id is the same as the id field of the Recipient object, then the member added is the agent, else it's a user. The agent's id is formatted as 28:<MicrosoftAppId>.
The following code shows an example of a team members added event:
app.OnMembersAdded(async context =>
{
foreach (var member in context.Activity.MembersAdded)
{
if (member.Id == context.Activity.Recipient.Id)
{
// Send a message to introduce the bot to the team.
await context.Send($"The {member.Name} agent has joined {context.Activity.ChannelData.Team.Name}");
}
else
{
await context.Send($"{member.Name} joined {context.Activity.ChannelData.Team.Name}");
}
}
});
app.on('membersAdded', async ({ activity, send }) => {
for (const member of activity.membersAdded) {
const teamName = activity.channelData?.team?.name ?? 'not in team';
await send(`${member.id} joined ${teamName}.`);
}
});
The message your agent receives when the agent is added to a team.
Note
In this payload, conversation.id and channelData.settings.selectedChannel.id are the IDs of the channel that the user selected during app installation or from which the installation was triggered.
{
"type": "conversationUpdate",
"membersAdded": [
{
"id": "28:608cacfd-1cea-40c9-b678-4b93e69bb72b"
}
],
"timestamp": "2021-12-07T22:34:56.534Z",
"id": "f:0b9079f4-d4d3-3d8e-b883-798298053c7e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer/",
"from": {
"id": "29:1ljv6N86roXr5pjPrCJVIz6xHh5QxjI....",
"aadObjectId": "eddfa9d4-346e-4cce-a18f-fa6261ad776b"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"tenantId": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f",
"id": "19:0b7f32667e064dd9b25d7969801541f4@thread.tacv2",
"name": "2021 Test Channel"
},
"recipient": {
"id": "28:608cacfd-1cea-40c9-b678-4b93e69bb72b",
"name": "Test Agent"
},
"channelData": {
"settings": {
"selectedChannel": {
"id": "19:0b7f32667e064dd9b25d7969801541f4@thread.tacv2"
}
},
"team": {
"aadGroupId": "f3ec8cd2-e704-4344-8c47-9a3a21d683c0",
"name": "TestTeam2022",
"id": "19:zFLSDFWsesfzcmKArqKJ-65aOXJz@sgf462H2wz41@thread.tacv2"
},
"eventType": "teamMemberAdded",
"tenant": {
"id": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
}
}
}
The message your agent receives when the agent is added to a one-to-one chat.
{
"membersAdded": [{
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0"
},
{
"id": "29:<userID>",
"aadObjectId": "***"
}
],
"type": "conversationUpdate",
"timestamp": "2019-04-23T10:17:44.349Z",
"id": "f:5f85c2ad",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:<USERID>",
"aadObjectId": "***"
},
"conversation": {
"conversationType": "personal",
"id": "***"
},
"recipient": {
"id": "28:<BOT ID>",
"name": "<BOT NAME>"
},
"channelData": {
"tenant": {
"id": "<TENANT ID>"
}
}
}
@app.on_conversation_update
async def handle_members_added(ctx: ActivityContext[ConversationUpdateActivity]):
if ctx.activity.members_added:
for member in ctx.activity.members_added:
await ctx.send(f"Welcome your new team member {member.id}")
Members removed
A member removed event is sent to your agent in the following scenarios:
- When the agent, itself, is uninstalled and removed from a conversation.
- When a user is removed from a conversation where the agent is installed.
The member removed activity eventType is set to teamMemberRemoved when the event is sent from a team context. To determine if the new member removed was the agent itself or a user, check the Activity object of the Context object. If the MembersRemoved list contains an object where id is the same as the id field of the Recipient object, then the member added is the agent, else it's a user. The agent's id is formatted as 28:<MicrosoftAppId>.
Note
When a user is permanently deleted from a tenant, membersRemoved conversationUpdate event is triggered.
The following code shows an example of a team members removed event:
app.OnMembersRemoved(async context =>
{
foreach (var member in context.Activity.MembersRemoved)
{
if (member.Id == context.Activity.Recipient.Id)
{
// The agent was removed.
// You should clear any cached data you have for this team.
}
else
{
await context.Send($"{member.Name} was removed from {context.Activity.ChannelData.Team.Name}");
}
}
});
app.on('membersRemoved', async ({ activity, send }) => {
for (const member of activity.membersRemoved) {
const teamName = activity.channelData?.team?.name ?? 'not in team';
await send(`${member.id} removed from ${teamName}.`);
}
});
The channelData object in the following payload example is based on adding a member to a team rather than a group chat, or initiating a new one-to-one conversation:
{
"membersRemoved": [
{
"id": "29:1_LCi5Up14pAy65yZuaJzG1uIT7ujYhjjSTsUNqjORsZHjLHKiQIBJa4cX2XsAsRoaY7va2w6ZymA9-1VtSY_g"
}
],
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:37:06.96Z",
"localTimestamp": "2017-02-23T12:37:06.96-07:00",
"id": "f:d8a6a4aa",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0OIy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient":
{
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterAgent"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"eventType": "teamMemberRemoved",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_conversation_update
async def handle_members_removed(ctx: ActivityContext[ConversationUpdateActivity]):
if ctx.activity.members_removed:
for member in ctx.activity.members_removed:
await ctx.send(f"Say goodbye to {member.id}")
Team renamed
Your agent is notified when the team is renamed. It receives a conversationUpdate event with eventType.teamRenamed in the channelData object.
The following code shows an example of a team renamed event:
app.OnTeamRenamed(async context =>
{
var teamName = context.Activity.ChannelData.Team.Name;
await context.Send($"{teamName} is the new Team name");
});
app.on('teamRenamed', async ({ activity, send }) => {
const teamName = activity.channelData.team.name;
await send(`${teamName} is the new Team name`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:35:56.825Z",
"localTimestamp": "2017-02-23T12:35:56.825-07:00",
"id": "f:1406033e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype",
"name": "New Team Name"
},
"eventType": "teamRenamed",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_team_renamed
async def handle_team_renamed(ctx: ActivityContext[ConversationUpdateActivity]):
team_name = ctx.activity.channel_data.team.name
await ctx.send(f"The new team name is {team_name}")
Team deleted
The agent receives a notification when the team is deleted. It receives a conversationUpdate event with eventType.teamDeleted in the channelData object.
The following code shows an example of a team deleted event:
app.OnTeamDeleted(async context =>
{
// Handle delete event.
});
app.on('teamDeleted', async ({ activity }) => {
// Handle delete event.
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:35:56.825Z",
"localTimestamp": "2017-02-23T12:35:56.825-07:00",
"id": "f:1406033e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype",
"name": "Team Name"
},
"eventType": "teamDeleted",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_team_deleted
async def handle_team_deleted(ctx: ActivityContext[ConversationUpdateActivity]):
# Handle delete event.
pass
Team restored
The agent receives a notification when a team is restored after being deleted. It receives a conversationUpdate event with eventType.teamrestored in the channelData object.
The following code shows an example of a team restored event:
app.OnTeamRestored(async context =>
{
var teamName = context.Activity.ChannelData.Team.Name;
await context.Send($"{teamName} is the team name");
});
app.on('teamRestored', async ({ activity, send }) => {
const teamName = activity.channelData.team.name;
await send(`${teamName} is the team name`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:35:56.825Z",
"localTimestamp": "2017-02-23T12:35:56.825-07:00",
"id": "f:1406033e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype",
"name": "Team Name"
},
"eventType": "teamrestored",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_team_restored
async def handle_team_restored(ctx: ActivityContext[ConversationUpdateActivity]):
team_name = ctx.activity.channel_data.team.name
await ctx.send(f"The team name is {team_name}")
Team archived
The agent receives a notification when the team is installed and archived. It receives a conversationUpdate event with eventType.teamarchived in the channelData object.
The following code shows an example of team archived event:
app.OnTeamArchived(async context =>
{
var teamName = context.Activity.ChannelData.Team.Name;
await context.Send($"{teamName} is the team name");
});
app.on('teamArchived', async ({ activity, send }) => {
const teamName = activity.channelData.team.name;
await send(`${teamName} is the team name`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:35:56.825Z",
"localTimestamp": "2017-02-23T12:35:56.825-07:00",
"id": "f:1406033e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype",
"name": "Team Name"
},
"eventType": "teamArchived",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_team_archived
async def handle_team_archived(ctx: ActivityContext[ConversationUpdateActivity]):
team_name = ctx.activity.channel_data.team.name
await ctx.send(f"The team name is {team_name}")
Team unarchived
The agent receives a notification when the team is installed and unarchived. It receives a conversationUpdate event with eventType.teamUnarchived in the channelData object.
The following code shows an example of a team unarchived event:
app.OnTeamUnarchived(async context =>
{
var teamName = context.Activity.ChannelData.Team.Name;
await context.Send($"{teamName} is the team name");
});
app.on('teamUnarchived', async ({ activity, send }) => {
const teamName = activity.channelData.team.name;
await send(`${teamName} is the team name`);
});
{
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:35:56.825Z",
"localTimestamp": "2017-02-23T12:35:56.825-07:00",
"id": "f:1406033e",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype",
"name": "Team Name"
},
"eventType": "teamUnarchived",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
@app.on_team_unarchived
async def handle_team_unarchived(ctx: ActivityContext[ConversationUpdateActivity]):
team_name = ctx.activity.channel_data.team.name
await ctx.send(f"The team name is {team_name}")
Now that you've worked with the conversation update events, you can understand the message reaction events that occur for different reactions to a message.
Message reaction events
The messageReaction event is sent when a user adds or removes reactions to a message, which was sent by your agent. The replyToId contains the ID of the message, and the Type is the type of reaction in text format. The types of reactions include angry, heart, laugh, like, sad, and surprised. This event doesn't contain the contents of the original message. If processing reactions to your messages is important for your agent, you must store the messages when you send them. The following table provides more information about the event type and payload objects:
Reactions added to agent message
The following code shows an example of reactions to an agent message:
app.OnReactionsAdded(async context =>
{
foreach (var reaction in context.Activity.ReactionsAdded)
{
var newReaction = $"You reacted with '{reaction.Type}' to the following message: '{context.Activity.ReplyToId}'";
await context.Send(newReaction);
}
});
app.on('reactionsAdded', async ({ activity, send }) => {
const reactionsAdded = activity.reactionsAdded;
if (reactionsAdded && reactionsAdded.length > 0) {
for (const reaction of reactionsAdded) {
const newReaction = `You reacted with '${reaction.type}' to the following message: '${activity.replyToId}'`;
await send(newReaction);
}
}
});
{
"reactionsAdded": [
{
"type": "like"
}
],
"type": "messageReaction",
"timestamp": "2017-10-16T18:45:41.943Z",
"id": "f:9f78d1f3",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA",
"aadObjectId": "c33aafc4-646d-4543-9d4c-abd28e4d2110"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:3629591d4b774aa08cb0887902eee7c1@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"channel": {
"id": "19:3629591d4b774aa08cb0887902eee7c1@thread.skype"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
},
"replyToId": "1575667808184",
"legacy": {
"replyToId": "1:19uJ8TZA1cZcms7-2HLOW3pWRF4nSWEoVnRqc0DPa_kY"
}
}
@app.on_reactions_added
async def handle_reactions_added(ctx: ActivityContext):
for reaction in ctx.activity.reactions_added:
await ctx.send(
f"You added '{reaction.type}' regarding message '{ctx.activity.reply_to_id}'"
)
Reactions removed from agent message
The following code shows an example of reactions removed from agent message:
app.OnReactionsRemoved(async context =>
{
foreach (var reaction in context.Activity.ReactionsRemoved)
{
var newReaction = $"You removed the reaction '{reaction.Type}' from the following message: '{context.Activity.ReplyToId}'";
await context.Send(newReaction);
}
});
app.on('reactionsRemoved', async ({ activity, send }) => {
const reactionsRemoved = activity.reactionsRemoved;
if (reactionsRemoved && reactionsRemoved.length > 0) {
for (const reaction of reactionsRemoved) {
const newReaction = `You removed the reaction '${reaction.type}' from the message: '${activity.replyToId}'`;
await send(newReaction);
}
}
});
{
"reactionsRemoved": [
{
"type": "like"
}
],
"type": "messageReaction",
"timestamp": "2017-10-16T18:45:41.943Z",
"id": "f:9f78d1f3",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA",
"aadObjectId": "c33aafc4-646d-4543-9d4c-abd28e4d2110"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:3629591d4b774aa08cb0887902eee7c1@thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"channel": {
"id": "19:3629591d4b774aa08cb0887902eee7c1@thread.skype"
},
"team": {
"id": "19:efa9296d959346209fea44151c742e73@thread.skype"
},
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
},
"replyToId": "1575667808184",
"legacy": {
"replyToId": "1:19uJ8TZA1cZcms7-2HLOW3pWRF4nSWEoVnRqc0DPa_kY"
}
}
@app.on_reactions_removed
async def handle_reactions_removed(ctx: ActivityContext):
for reaction in ctx.activity.reactions_removed:
await ctx.send(
f"You removed '{reaction.type}' regarding message '{ctx.activity.reply_to_id}'"
)
Installation update event
The agent receives an installationUpdate event when you install an agent to a conversation thread. Uninstallation of the agent from the thread also triggers the event. On installing an agent, the action field in the event is set to add, and when the agent is uninstalled the action field is set to remove.
Note
When you upgrade an application, the agent receives the installationUpdate event only to add or remove an agent from the manifest. For all other cases, the installationUpdate event isn't triggered. The action field is set to add-upgrade if you add an agent or remove-upgrade if you remove an agent.
Install update event
Use the installationUpdate event to send an introductory message from your agent on installation. This event helps you to meet your privacy and data retention requirements. You can also clean up and delete user or thread data when the agent is uninstalled.
Similar to the conversationUpdate event that's sent when agent is added to a team, the conversation.id of the installationUpdate event is set to the id of the channel selected by a user during app installation or the channel where the installation occurred. The id represents the channel where the user intends for the agent to operate and must be used by the agent when sending a welcome message. For scenarios where the ID of the General channel is explicitly required, you can get it from team.id in channelData.
In this example, the conversation.id of the conversationUpdate and installationUpdate activities is set to the ID of the Response channel in the Daves Demo team.
Note
The selected channel id is only set on installationUpdate add events that are sent when an app is installed into a team.
app.OnInstall(async context =>
{
var action = context.Activity.Action;
if (string.Equals(action, "Add", StringComparison.InvariantCultureIgnoreCase))
{
await context.Send("Added");
}
else
{
await context.Send("Uninstalled");
}
});
You can also use a dedicated handler for add or remove scenarios as an alternative method to capture an event.
protected override async Task OnInstallationUpdateAddAsync(IContext<IInstallationUpdateActivity> Context object, CancellationToken cancellationToken)
{
// TO:DO Installation workflow return;
}
app.on('install.add', async ({ send }) => {
await send('Added');
});
app.on('install.remove', async ({ send }) => {
await send('Uninstalled');
});
{
{
"type": "installationUpdate",
"id": "f:816eb23d-bfa1-afa3-dfeb-d2aa338e9541",
"timestamp": "2021-11-09T04:47:30.91Z",
"serviceUrl": "https://smba.trafficmanager.net/amer/",
"channelId": "msteams",
"from": {
"id": "29:1ljv6N86roXr5pjPrCJVIz6xHh5QxjI....",
"aadObjectId": "eddfa9d4-346e-4cce-a18f-fa6261ad776b"
},
"recipient": {
"id": "28:608cacfd-1cea-40c9-b678-4b93e69bb72b",
"name": "Test Agent"
},
"locale": "en-US",
"entities": [
{
"type": "clientInfo",
"locale": "en-US"
}
],
"conversation": {
"isGroup": true,
"id": "19:0b7f32667e064dd9b25d7969801541f4@thread.tacv2",
"name": "2021 Test Channel",
"conversationType": "channel",
"tenantId": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
},
"channelData": {
"settings": {
"selectedChannel": {
"id": "19:0b7f32667e064dd9b25d7969801541f4@thread.tacv2"
}
},
"channel": {
"id": "19:0b7f32667e064dd9b25d7969801541f4@thread.tacv2"
},
"team": {
"aadGroupId": "da849743-4259-475f-ae7a-4f4b0fb49943",
"name": "TestTeam2022",
"id": "19:zFLSDFWsesfzcmKArqKJ-65aOXJz@sgf462H2wz41@thread.tacv2"
},
"tenant": {
"id": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
},
"source": {
"name": "message"
}
},
"action": "add"
}
@app.on_install_add
async def handle_install_add(ctx: ActivityContext):
await ctx.send("Added")
@app.on_install_remove
async def handle_install_remove(ctx: ActivityContext):
await ctx.send("Uninstalled")
Uninstall behavior for personal app with agent
When you uninstall an app, the agent is also uninstalled. When a user sends a message to your app, they receive a 403 response code. Your agent receives a 403 response code for new messages posted by your agent. The post uninstall behavior for agents in the personal scope with the Teams and groupChat scopes are now aligned. You can't send or receive messages after an app has been uninstalled.
Event handling for install and uninstall events
When you use the install and uninstall events, there are some instances where agents give exceptions on receiving unexpected events from Teams, which occurs in the following cases:
- You build your agent without Teams SDK, and as a result the agent gives an exception on receiving an unexpected event.
- You build your agent with Teams SDK, and you select to alter the default event behavior by overriding the base event handle.
It's important to know that new events can be added anytime in the future and your agent begins to receive them. So you must design for the possibility of receiving unexpected events. If you're using Teams SDK, your agent automatically responds with a 200 - OK to any events you don't choose to handle.
Handling errors in conversation events
When an agent encounters an error while handling different events or activities, don't send messages that have no meaningful context to the conversation as shown in the following screenshot:
In the development phase, it's always helpful to send meaningful messages in conversations, which provide additional details about a specific error for better debugging. However, in the production environment, you must log the errors or events to Azure Application Insights. For more information, see add telemetry to your bot.
Code sample
| Sample Name |
Description |
.NET |
Node.js |
Python |
| Conversation bot |
This app demonstrates bot conversation events, supporting Adaptive Cards, read receipts, and message update events. It includes immersive reader support for accessibility. |
View |
View |
View |
Next step