Важно!
Примеры кода в этом разделе основаны на версии 4.6 и более поздних версиях пакета SDK Bot Framework. Если вы ищете документацию по более ранним версиям, см. раздел пакет SDK для ботов версии 3 в папке Устаревшие пакеты SDK документации.
При создании чат-ботов для Microsoft Teams можно работать с событиями беседы. Microsoft Teams отправляет вашему боту уведомления о событиях в тех областях, где этот бот активен. Эти события можно обрабатывать программно и выполнять с ними те или иные действия, например:
- отправлять приветственное сообщение при добавлении бота в команду;
- отправлять приветственное сообщение при добавлении пользователя в команду или его удалении;
- отправлять уведомление при создании, переименовании или удалении канала;
- отправлять уведомление, когда пользователь ставит сообщению бота отметку «Нравится».
- Определите канал по умолчанию для бота из пользовательского ввода (выбора) во время установки.
В следующем видео показано, как бот беседы может улучшить взаимодействие с клиентами за счет плавного и интеллектуального взаимодействия:
События обновления беседы
События обновления беседы можно использовать для предоставления более качественных уведомлений и эффективных действий бота.
Важно!
- Вы можете добавлять новые события в любое время, и бот начнет их получать.
- Разрабатывая бот, учитывайте возможность получения непредвиденных событий.
- Если вы используете пакет SDK Bot Framework, бот автоматически отвечает на любые события, которые вы решили не обрабатывать, отправкой
200 - OK
.
- Когда клиент Службы коммуникации Azure (ACS) присоединяется к собранию Teams или покидает его, события обновления диалога не активируются.
Бот получает событие conversationUpdate
во всех перечисленных ниже случаях:
- При добавлении бота в беседу.
- При добавлении в беседу или удалении из нее других участников.
- При изменении метаданных беседы.
Событие conversationUpdate
отправляется боту, когда поступают сведения об обновлении членства в командах, куда он добавлен. Бот также получает обновление при первом добавлении в личную беседу.
В следующей таблице приведен список событий Teams обновления беседы с дополнительными сведениями:
Канал создан
Событие channelCreated
отправляется боту каждый раз, когда создается новый канал в команде, в которой установлен бот.
В следующем коде показан пример события, созданного каналом:
protected override async Task OnTeamsChannelCreatedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the Channel created");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsChannelCreatedEvent(async (channelInfo: ChannelInfo, teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Channel Created', `${channelInfo.name} is the Channel created`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:[email protected]",
"name": "FunDiscussions"
},
"team": {
"id": "19:[email protected]"
},
"eventType": "channelCreated",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
async def on_teams_channel_created(
self, channel_info: ChannelInfo, team_info: TeamInfo, turn_context: TurnContext
):
# Sends a message activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(
f"The new channel is {channel_info.name}. The channel id is {channel_info.id}"
)
)
Канал переименован
Событие channelRenamed
отправляется боту при каждом переименовании канала в команде, в которой установлен бот.
В следующем коде показан пример события переименования канала:
protected override async Task OnTeamsChannelRenamedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the new Channel name");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsChannelRenamedEvent(async (channelInfo: ChannelInfo, teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Channel Renamed', `${channelInfo.name} is the new Channel name`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:[email protected]",
"name": "PhotographyUpdates"
},
"team": {
"id": "19:[email protected]"
},
"eventType": "channelRenamed",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
async def on_teams_channel_renamed(
self, channel_info: ChannelInfo, team_info: TeamInfo, turn_context: TurnContext
):
return await turn_context.send_activity(
MessageFactory.text(f"The new channel name is {channel_info.name}")
)
Канал удален
Событие channelDeleted
отправляется боту каждый раз, когда канал удаляется в команде, где установлен бот.
В следующем коде показан пример события удаления канала:
protected override async Task OnTeamsChannelDeletedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the Channel deleted");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsChannelDeletedEvent(async (channelInfo: ChannelInfo, teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Channel Deleted', `${channelInfo.name} is the Channel deleted`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:[email protected]",
"name": "PhotographyUpdates"
},
"team": {
"id": "19:[email protected]"
},
"eventType": "channelDeleted",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
async def on_teams_channel_deleted(
self, channel_info: ChannelInfo, team_info: TeamInfo, turn_context: TurnContext
):
# Sends a message activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(f"The deleted channel is {channel_info.name}")
)
Канал восстановлен
Событие channelRestored
отправляется боту каждый раз, когда ранее удаленный канал восстанавливается в команде, где бот уже установлен.
В следующем коде показан пример события восстановления канала:
protected override async Task OnTeamsChannelRestoredAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the Channel restored.");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsChannelRestoredEvent(async (channelInfo: ChannelInfo, teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Channel Restored', `${channelInfo.name} is the Channel restored`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"channel": {
"id": "19:[email protected]",
"name": "FunDiscussions"
},
"team": {
"id": "19:[email protected]"
},
"eventType": "channelRestored",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
async def on_teams_channel_restored(
self, channel_info: ChannelInfo, team_info: TeamInfo, turn_context: TurnContext
):
# Sends a message activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(
f"The restored channel is {channel_info.name}. The channel id is {channel_info.id}"
)
)
Участники добавлены
Добавленное событие участника отправляется боту в следующих сценариях:
При установке самого бота и добавлении в беседу
В контексте команды в качестве conversation.id действия задается id
канал, выбранный пользователем во время установки приложения, или канал, в котором был установлен бот.
При добавлении пользователя в беседу, в которой установлен бот
Идентификаторы пользователей, полученные в полезных данных события, являются уникальными для бота и могут кэшироваться для использования в будущем, например для непосредственного обмена сообщениями с пользователем.
При отправке события из контекста команды для добавленного участника действия eventType
задается teamMemberAdded
значение . Чтобы определить, является ли добавленный новый элемент самим ботом или пользователем, проверка Activity
объект объекта turnContext
. Если список MembersAdded
содержит объект , где id
совпадает с полем id
Recipient
объекта, то добавленный элемент является ботом, в противном случае это пользователь. Бот имеет id
формат 28:<MicrosoftAppId>
.
В следующем коде показан пример добавленного события участниками команды:
protected override async Task OnTeamsMembersAddedAsync(IList<TeamsChannelAccount> teamsMembersAdded , TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (TeamsChannelAccount member in teamsMembersAdded)
{
if (member.Id == turnContext.Activity.Recipient.Id)
{
// Send a message to introduce the bot to the team.
var heroCard = new HeroCard(text: $"The {member.Name} bot has joined {teamInfo.Name}");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
else
{
var heroCard = new HeroCard(text: $"{member.Name} joined {teamInfo.Name}");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
}
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsMembersAddedEvent(async (membersAdded: ChannelAccount[], teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
let newMembers: string = '';
console.log(JSON.stringify(membersAdded));
membersAdded.forEach((account) => {
newMembers += account.id + ' ';
});
const name = !teamInfo ? 'not in team' : teamInfo.name;
const card = CardFactory.heroCard('Account Added', `${newMembers} joined ${name}.`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
Сообщение, которое бот получает при добавлении бота в команду.
Примечание.
В этой полезных данных и channelData.settings.selectedChannel.id
находятся идентификаторы канала, conversation.id
выбранного пользователем во время установки приложения или из которого была активирована установка.
{
"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:[email protected]",
"name": "2021 Test Channel"
},
"recipient": {
"id": "28:608cacfd-1cea-40c9-b678-4b93e69bb72b",
"name": "Test Bot"
},
"channelData": {
"settings": {
"selectedChannel": {
"id": "19:[email protected]"
}
},
"team": {
"aadGroupId": "f3ec8cd2-e704-4344-8c47-9a3a21d683c0",
"name": "TestTeam2022",
"id": "19:zFLSDFWsesfzcmKArqKJ-65aOXJz@[email protected]"
},
"eventType": "teamMemberAdded",
"tenant": {
"id": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
}
}
}
Сообщение, которое бот получает при добавлении бота в чат "один к одному".
{
"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>"
}
}
}
async def on_teams_members_added(
self, teams_members_added: [TeamsChannelAccount], turn_context: TurnContext
):
for member in teams_members_added:
.. # Sends a message activity to the sender of the incoming activity.
await turn_context.send_activity(
MessageFactory.text(f"Welcome your new team member {member.id}")
)
return
Элементы удалены
Событие удаления участника отправляется боту в следующих сценариях:
- Когда бот удаляется и удаляется из беседы.
- При удалении пользователя из беседы, в которой установлен бот.
При отправке события из контекста команды для eventType
действия, удаленного участника, задается teamMemberRemoved
значение . Чтобы определить, был ли только что удаленный участник самим ботом или пользователем, проверьте объект Activity
из turnContext
. Если список MembersRemoved
содержит объект , где id
совпадает с полем id
Recipient
объекта, то добавленный элемент является ботом, в противном случае это пользователь. Идентификатор бота форматируется как 28:<MicrosoftAppId>
.
Примечание.
При окончательном удалении пользователя из клиента инициируется событие membersRemoved conversationUpdate
.
В следующем коде показан пример удаленного события участников команды:
protected override async Task OnTeamsMembersRemovedAsync(IList<ChannelAccount> membersRemoved, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (TeamsChannelAccount member in membersRemoved)
{
if (member.Id == turnContext.Activity.Recipient.Id)
{
// The bot was removed.
// You should clear any cached data you have for this team.
}
else
{
var heroCard = new HeroCard(text: $"{member.Name} was removed from {teamInfo.Name}");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
}
}
Справочные материалы для SDK
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsMembersRemovedEvent(async (membersRemoved: ChannelAccount[], teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
let removedMembers: string = '';
console.log(JSON.stringify(membersRemoved));
membersRemoved.forEach((account) => {
removedMembers += account.id + ' ';
});
const name = !teamInfo ? 'not in team' : teamInfo.name;
const card = CardFactory.heroCard('Account Removed', `${removedMembers} removed from ${teamInfo.name}.`);
const message = MessageFactory.attachment(card);
// Sends a message activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
Объект channelData
в следующем примере полезных данных основан на добавлении участника в команду, а не в групповой чат и не запуск новой беседы "один к одному":
{
"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:[email protected]"
},
"recipient":
{
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
"team": {
"id": "19:[email protected]"
},
"eventType": "teamMemberRemoved",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
async def on_teams_members_removed(
self, teams_members_removed: [TeamsChannelAccount], turn_context: TurnContext
):
for member in teams_members_removed:
..# Sends a message activity to the sender of the incoming activity.
await turn_context.send_activity(
MessageFactory.text(f"Say goodbye to {member.id}")
)
return
Команда переименована
Бот получает уведомление при переименовании команды. Он получает событие conversationUpdate
с eventType.teamRenamed
в объекте channelData
.
В следующем коде показан пример события, переименованного командой:
protected override async Task OnTeamsTeamRenamedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{teamInfo.Name} is the new Team name");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Bot is notified when the team is renamed.
this.onTeamsTeamRenamedEvent(async (teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Team Renamed', `${teamInfo.name} is the new Team name`);
const message = MessageFactory.attachment(card);
// Sends an activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:[email protected]",
"name": "New Team Name"
},
"eventType": "teamRenamed",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
# Bot is notified when the team is renamed.
async def on_teams_team_renamed(
self, team_info: TeamInfo, turn_context: TurnContext
):
# Sends an activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(f"The new team name is {team_info.name}")
)
Команда удалена
При удалении команды бот получает уведомление. Он получает событие conversationUpdate
с eventType.teamDeleted
в объекте channelData
.
В следующем коде показан пример события, удаленного командой:
protected override async Task OnTeamsTeamDeletedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
// Handle delete event.
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Invoked when a Team Deleted event activity is received from the connector. Team Deleted corresponds to the user deleting a team.
this.onTeamsTeamDeletedEvent(async (teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
// Handle delete event.
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:[email protected]",
"name": "Team Name"
},
"eventType": "teamDeleted",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
# Invoked when a Team Deleted event activity is received from the connector. Team Deleted corresponds to the user deleting a team.
async def on_teams_team_deleted(
self, team_info: TeamInfo, turn_context: TurnContext
):
# Handle delete event.
)
Команда восстановлена
Бот получает уведомление о восстановлении команды после удаления. Он получает событие conversationUpdate
с eventType.teamrestored
в объекте channelData
.
В следующем коде показан пример события восстановления команды:
protected override async Task OnTeamsTeamrestoredAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{teamInfo.Name} is the team name");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Invoked when a Team Restored event activity is received from the connector. Team Restored corresponds to the user restoring a team.
this.onTeamsTeamrestoredEvent(async (teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Team restored', `${teamInfo.name} is the team name`);
const message = MessageFactory.attachment(card);
// Sends an activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:[email protected]",
"name": "Team Name"
},
"eventType": "teamrestored",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
# Invoked when a Team Restored event activity is received from the connector. Team Restored corresponds to the user restoring a team.
async def on_teams_team_restored(
self, team_info: TeamInfo, turn_context: TurnContext
):
# Sends an activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(f"The team name is {team_info.name}")
)
Команда архивирована
Бот получает уведомление о том, что команда установлена и архивирована. Он получает событие conversationUpdate
с eventType.teamarchived
в объекте channelData
.
В следующем фрагменте программного кода показан пример события "команда архивирована".
protected override async Task OnTeamsTeamArchivedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{teamInfo.Name} is the team name");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Invoked when a Team Archived event activity is received from the connector. Team Archived.
this.onTeamsTeamArchivedEvent(async (teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Team archived', `${teamInfo.name} is the team name`);
const message = MessageFactory.attachment(card);
// Sends an activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:[email protected]",
"name": "Team Name"
},
"eventType": "teamArchived",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
# Invoked when a Team Archived event activity is received from the connector. Team Archived correspond to the user archiving a team.
async def on_teams_team_archived(
self, team_info: TeamInfo, turn_context: TurnContext
):
# Sends an activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(f"The team name is {team_info.name}")
)
Архивация команды отменена
Бот получает уведомление об установке команды и восстановлении ее из архива. Он получает событие conversationUpdate
с eventType.teamUnarchived
в объекте channelData
.
В следующем коде показан пример неархивированного события команды:
protected override async Task OnTeamsTeamUnarchivedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{teamInfo.Name} is the team name");
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Invoked when a Team Unarchived event activity is received from the connector. Team.
this.onTeamsTeamUnarchivedEvent(async (teamInfo: TeamInfo, turnContext: TurnContext, next: () => Promise<void>): Promise<void> => {
const card = CardFactory.heroCard('Team archived', `${teamInfo.name} is the team name`);
const message = MessageFactory.attachment(card);
// Sends an activity to the sender of the incoming activity.
await turnContext.sendActivity(message);
await next();
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"team": {
"id": "19:[email protected]",
"name": "Team Name"
},
"eventType": "teamUnarchived",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
# Invoked when a Team Unarchived event activity is received from the connector. Team Unarchived correspond to the user unarchiving a team.
async def on_teams_team_unarchived(
self, team_info: TeamInfo, turn_context: TurnContext
):
# Sends an activity to the sender of the incoming activity.
return await turn_context.send_activity(
MessageFactory.text(f"The team name is {team_info.name}")
)
Теперь, когда вы работали с событиями обновления беседы, вы можете понять события реакции сообщения, которые происходят для различных реакций на сообщение.
События реакции на сообщение
Событие messageReaction
отправляется, когда пользователь добавляет или удаляет реакции на сообщение, отправленное ботом.
replyToId
содержит идентификатор сообщения, а Type
- тип реакции в текстовом формате. В список типов реакций входят: гнев, любовь, смех, "нравится", печаль и удивление. Это событие не содержит содержимое исходного сообщения. Если обработка реакций на сообщения - важная функция бота, сохраняйте их при отправке. В следующей таблице приведены дополнительные сведения о типе события и объектах полезных данных:
Реакции, добавленные в сообщение бота
В следующем фрагменте программного кода показан пример реакции на сообщение бота:
protected override async Task OnReactionsAddedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var reaction in messageReactions)
{
var newReaction = $"You reacted with '{reaction.Type}' to the following message: '{turnContext.Activity.ReplyToId}'";
var replyActivity = MessageFactory.Text(newReaction);
// Sends an activity to the sender of the incoming activity.
var resourceResponse = await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Override this in a derived class to provide logic for when reactions to a previous activity.
this.onReactionsAdded(async (context, next) => {
const reactionsAdded = context.activity.reactionsAdded;
if (reactionsAdded && reactionsAdded.length > 0) {
for (let i = 0; i < reactionsAdded.length; i++) {
const reaction = reactionsAdded[i];
const newReaction = `You reacted with '${reaction.type}' to the following message: '${context.activity.replyToId}'`;
// Sends an activity to the sender of the incoming activity.
const resourceResponse = context.sendActivity(newReaction);
// Save information about the sent message and its ID (resourceResponse.id).
}
}
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"channel": {
"id": "19:[email protected]"
},
"team": {
"id": "19:[email protected]"
},
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
},
"replyToId": "1575667808184",
"legacy": {
"replyToId": "1:19uJ8TZA1cZcms7-2HLOW3pWRF4nSWEoVnRqc0DPa_kY"
}
}
# Override this in a derived class to provide logic for when reactions to a previous activity are added to the conversation.
async def on_reactions_added(
self, message_reactions: List[MessageReaction], turn_context: TurnContext
):
for reaction in message_reactions:
activity = await self._log.find(turn_context.activity.reply_to_id)
if not activity:
# Sends an activity to the sender of the incoming activity.
await self._send_message_and_log_activity_id(
turn_context,
f"Activity {turn_context.activity.reply_to_id} not found in log",
)
else:
# Sends an activity to the sender of the incoming activity.
await self._send_message_and_log_activity_id(
turn_context,
f"You added '{reaction.type}' regarding '{activity.text}'",
)
return
Реакции, удаленные из сообщения бота
В следующем фрагменте программного кода показан пример реакций, удаленных из сообщения бота:
protected override async Task OnReactionsRemovedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var reaction in messageReactions)
{
var newReaction = $"You removed the reaction '{reaction.Type}' from the following message: '{turnContext.Activity.ReplyToId}'";
var replyActivity = MessageFactory.Text(newReaction);
// Sends an activity to the sender of the incoming activity.
var resourceResponse = await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
}
export class MyBot extends TeamsActivityHandler {
constructor() {
super();
// Override this in a derived class to provide logic for when reactions to a previous activity.
this.onReactionsRemoved(async(context,next)=>{
const reactionsRemoved = context.activity.reactionsRemoved;
if (reactionsRemoved && reactionsRemoved.length > 0) {
for (let i = 0; i < reactionsRemoved.length; i++) {
const reaction = reactionsRemoved[i];
const newReaction = `You removed the reaction '${reaction.type}' from the message: '${context.activity.replyToId}'`;
// Sends an activity to the sender of the incoming activity.
const resourceResponse = context.sendActivity(newReaction);
// Save information about the sent message and its ID (resourceResponse.id).
}
}
});
}
}
{
"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:[email protected]"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterLocal"
},
"channelData": {
"channel": {
"id": "19:[email protected]"
},
"team": {
"id": "19:[email protected]"
},
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
},
"replyToId": "1575667808184",
"legacy": {
"replyToId": "1:19uJ8TZA1cZcms7-2HLOW3pWRF4nSWEoVnRqc0DPa_kY"
}
}
# Override this in a derived class to provide logic specific to removed activities.
async def on_reactions_removed(
self, message_reactions: List[MessageReaction], turn_context: TurnContext
):
for reaction in message_reactions:
activity = await self._log.find(turn_context.activity.reply_to_id)
if not activity:
# Sends an activity to the sender of the incoming activity.
await self._send_message_and_log_activity_id(
turn_context,
f"Activity {turn_context.activity.reply_to_id} not found in log",
)
else:
# Sends an activity to the sender of the incoming activity.
await self._send_message_and_log_activity_id(
turn_context,
f"You removed '{reaction.type}' regarding '{activity.text}'",
)
return
Событие обновления установки
Бот получает событие installationUpdate
при установке бота в поток беседы. Удаление бота из потока также генерирует это событие. При установке бота полю действие в событии присваивается значение add, а при удалении полю действие присваивается значение remove.
Примечание.
При обновлении приложения бот получает installationUpdate
событие только для добавления или удаления бота из манифеста. Во всех остальных случаях installationUpdate
событие не активируется. При добавлении бота полю действие в событии присваивается значение add-upgrade, а при удалении - значение remove-upgrade.
Событие установки обновления
Используйте событие installationUpdate
для отправки вводного сообщения от бота при установке. Это событие помогает обеспечить соответствие требованиям к конфиденциальности и хранению данных. При удалении бота можно также очистить и удалить данные пользователя или потока.
Аналогично событию conversationUpdate
, которое отправляется при добавлении бота в команду, в качестве conversation.id installationUpdate
события устанавливается идентификатор канала, выбранного пользователем во время установки приложения или канала, в котором произошла установка. Идентификатор представляет канал, в котором пользователь намерен работать с ботом, и должен использоваться ботом при отправке приветственного сообщения. В случаях, когда идентификатор канала "Общий" явно требуется, его можно получить в team.id
channelData
.
В этом примере conversation.id
conversationUpdate
для действий и installationUpdate
задается идентификатор канала ответа в команде Daves Demo.
Примечание.
Выбранный идентификатор канала устанавливается только для installationUpdate
событий добавления , которые отправляются при установке приложения в команду.
protected override async Task OnInstallationUpdateActivityAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
if (string.Equals(activity.Action, "Add", StringComparison.InvariantCultureIgnoreCase))
{
// TO:DO Installation workflow.
}
else
{
// TO:DO Uninstallation workflow.
}
return;
}
Можно также использовать выделенный обработчик добавления или удаления сценариев в качестве альтернативного метода для записи события.
protected override async Task OnInstallationUpdateAddAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
// TO:DO Installation workflow return;
}
async onInstallationUpdateActivity(context: TurnContext) {
var activity = context.activity.action;
if(activity == "Add") {
// Sends an activity to the sender of the incoming activity to add.
await context.sendActivity(MessageFactory.text("Added"));
}
else {
// Sends an activity to the sender of the incoming activity to uninstalled.
await context.sendActivity(MessageFactory.text("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 Bot"
},
"locale": "en-US",
"entities": [
{
"type": "clientInfo",
"locale": "en-US"
}
],
"conversation": {
"isGroup": true,
"id": "19:[email protected]",
"name": "2021 Test Channel",
"conversationType": "channel",
"tenantId": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
},
"channelData": {
"settings": {
"selectedChannel": {
"id": "19:[email protected]"
}
},
"channel": {
"id": "19:[email protected]"
},
"team": {
"aadGroupId": "da849743-4259-475f-ae7a-4f4b0fb49943",
"name": "TestTeam2022",
"id": "19:zFLSDFWsesfzcmKArqKJ-65aOXJz@[email protected]"
},
"tenant": {
"id": "b28fdbfd-2b78-4f93-b0f8-8881793f0f8f"
},
"source": {
"name": "message"
}
},
"action": "add"
}
# Override this in a derived class to provide logic specific to InstallationUpdate activities.
async def on_installation_update(self, turn_context: TurnContext):
if turn_context.activity.action == "add":
# Sends an activity to the sender of the incoming activity to add.
await turn_context.send_activity(MessageFactory.text("Added"))
else:
# Sends an activity to the sender of the incoming activity to uninstalled.
await turn_context.send_activity(MessageFactory.text("Uninstalled"))
Поведение при удалении для личного приложения с ботом
При удалении приложения бот также удаляется. Отправив сообщение вашему приложению, пользователь получает код ответа 403. Бот получает код 403 в качестве ответа на новые сообщения, опубликованные им самим. Поведение после удаления ботов в области личного чата теперь согласовано с поведением в аналогичной ситуации в областях Teams и группового чата. Вы не сможете отправлять или получать сообщения после удаления приложения.
Обработка событий установки и удаления
При использовании событий установки и удаления в некоторых случаях боты выдают исключения при получении непредвиденных событий из Teams, что происходит в следующих случаях:
- Вы создаете бот без Microsoft Bot Framework SDK, и в результате бот выдает исключение при получении непредвиденного события.
- Вы создаете бот с помощью Microsoft Bot Framework SDK и решаете изменить обработку события по умолчанию путем переопределения базового дескриптора события.
Важно знать, что новые события могут быть добавлены в любое время в будущем, и ваш бот начнет получать их. Поэтому необходимо спроектировать возможность получения непредвиденных событий. Если вы используете пакет SDK Bot Framework, бот автоматически отвечает 200 –ОК на любые события, которые вы не решили обрабатывать.
Обработка ошибок в событиях диалога
Когда бот обнаруживает ошибку при обработке различных событий или действий, не отправляйте сообщения, не имеющие осмысленного контекста, в беседу, как показано на следующем снимке экрана:
На этапе разработки всегда полезно отправлять содержательные сообщения в беседах, которые предоставляют дополнительные сведения о конкретной ошибке для лучшей отладки. Однако в рабочей среде необходимо регистрировать ошибки или события, чтобы приложение Azure Insights. Дополнительные сведения см. в статье Добавление данных телеметрии в бот.
Пример кода
Название примера |
Описание |
.NET |
Node.js |
Python |
Манифест |
Бот беседы |
Это приложение демонстрирует события бесед бота, поддерживающие адаптивные карточки, уведомления о прочтении и события обновления сообщений. Он включает в себя поддержку иммерсивного чтения для специальных возможностей. |
Просмотр |
Просмотр |
Просмотр |
Просмотр |
Следующий этап
См. также