Have a conversation with a Microsoft Teams bot
Important
This article is based on the v3 Bot Framework SDK. If you are looking for current documentation version 4.6 or later of the SDK, see the conversational bots section.
A conversation is a series of messages sent between your bot and one or more users. There are three kinds of conversations (also called scopes) in Teams:
teams
Also called channel conversations, visible to all members of the channel.personal
Conversations between bots and a single user.groupChat
Chat between a bot and two or more users.
A bot behaves slightly differently depending on what kind of conversation it's involved in:
- Bots in channel and group chat conversations require the user to @mention the bot to invoke it in a channel.
- Bots in single user conversations don't require an @mention - the user can just type.
In order for the bot to work in a particular scope it should be listed as supporting that scope in the manifest. Scopes are defined and discussed further in the Manifest Reference.
Proactive messages
Bots can participate in a conversation or initiate one. Most communication is in response to another message. If a bot initiates a conversation it's called a proactive message. Examples include:
- Welcome messages
- Event notifications
- Polling messages
Conversation basics
Each message is an Activity
object of type messageType: message
. When a user sends a message, Teams posts the message to your bot; specifically, it sends a JSON object to your bot's messaging endpoint. Your bot examines the message to determine its type and responds accordingly.
Bots also support event-style messages. For more information, see Handle bot events in Microsoft Teams. Speech isn't supported.
Messages are usually the same in across all scopes, but there are differences in how the bot is accessed in the UI and differences behind the scenes, which you need to know about.
Basic conversation is handled through the Bot Framework Connector, a single REST API to enable your bot to communicate with Teams and other channels. The Bot Builder SDK provides easy access to this API, additional functionality to manage conversation flow and state, and simple ways to incorporate cognitive services such as natural language processing (NLP).
Message content
Your bot can send rich text, pictures, and cards. Users can send rich text and pictures to your bot. You can specify the type of content your bot can handle in the Microsoft Teams settings page for your bot.
Format | From user to bot | From bot to user | Notes |
---|---|---|---|
Rich text | ✔ | ✔ | |
Pictures | ✔ | ✔ | Maximum 1024×1024 MB and 1 MB in PNG, JPEG, or GIF format; animated GIF aren't supported. |
Cards | ✖ | ✔ | See the Teams Card Reference for supported cards. |
Emojis | ✖ | ✔ | Teams supports emojis via UTF-16 such as, U+1F600 for grinning face. |
For more information on the types of bot interaction supported by the Bot Framework, which bots in teams are based on, see the Bot Framework documentation on conversation flow and related concepts in the documentation for the Bot Builder SDK for .NET and the Bot Builder SDK for Node.js.
Message formatting
You can set the optional TextFormat
property of a message
to control how your message's text content is rendered. See Message formatting for a detailed description of supported formatting in bot messages.
You can set the optional TextFormat
property to control how your message's text content is rendered.
For detailed information on how Teams supports text formatting in teams see Text formatting in bot messages.
For more information on formatting cards in messages, see Card formatting.
Picture messages
Pictures are sent by adding attachments to a message. You can find more information on attachments in the Bot Framework documentation.
Pictures can be at most 1024×1024 MB and 1 MB in PNG, JPEG, or GIF format; animated GIF isn't supported.
We recommend that you specify the height and width of each image by using XML. If you use Markdown, the image size defaults to 256×256. For example:
- Use
<img src="http://aka.ms/Fo983c" alt="Duck on a rock" height="150" width="223"></img>
- Don't use
![Duck on a rock](http://aka.ms/Fo983c)
Receiving messages
Depending on which scopes are declared, your bot can receive messages in the following contexts:
- personal chat Users can interact in a private conversation with a bot by selecting the added bot in the chat history, or typing its name or app ID in the To: box on a new chat.
- Channels A bot can be mentioned ("@botname") in a channel if it has been added to the team. Note that additional replies to a bot in a channel require mentioning the bot. It will not respond to replies where it isn't mentioned.
For incoming messages, your bot receives an Activity object of type messageType: message
. Although the Activity
object can contain other types of information, like channel updates sent to your bot, the message
type represents communication between bot and user.
Your bot receives a payload that contains the user message Text
and other information about the user, the source of the message, and Teams information. Of note:
timestamp
The date and time of the message in Coordinated Universal Time (UTC).localTimestamp
The date and time of the message in the time zone of the sender.channelId
Always "msteams". This refers to a bot framework channel, not a teams channel.from.id
A unique and encrypted ID for that user for your bot; suitable as a key if your app needs to store user data. It's unique for your bot and can't be directly used outside your bot instance in any meaningful way to identify that user.channelData.tenant.id
The tenant ID for the user.
Note
from.id
is unique for your bot and cannot be directly used outside your bot instance in any meaningful way to identify that user.
Combining channel and private interactions with your bot
When interacting in a channel, your bot should be smart about taking certain conversations offline with a user. For instance, suppose a user is trying to coordinate a complex task, such as scheduling with a set of team members. Rather than have the entire sequence of interactions visible to the channel, consider sending a personal chat message to the user. Your bot should be able to easily transition the user between personal and channel conversations without losing state.
Note
Don’t forget to update the channel when the interaction is complete to notify the other team members.
Full inbound schema example
{
"type": "message",
"id": "1485983408511",
"timestamp": "2017-02-01T21:10:07.437Z",
"localTimestamp": "2017-02-01T14:10:07.437-07:00",
"serviceUrl": "https://smba.trafficmanager.net/amer/",
"channelId": "msteams",
"from": {
"id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB39ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
"name": "Megan Bowen",
"aadObjectId": "7faf8ab2-3d56-4244-b585-20c8a42ed2b8"
},
"conversation": {
"conversationType": "personal",
"id": "a:17I0kl9EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-"
},
"recipient": {
"id": "28:c9e8c047-2a74-40a2-b28a-b162d5f5327c",
"name": "Teams TestBot"
},
"textFormat": "plain",
"text": "Hello Teams TestBot",
"entities": [
{
"locale": "en-US",
"country": "US",
"platform": "Windows",
"timezone": "America/Los_Angeles",
"type": "clientInfo"
}
],
"channelData": {
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
},
"locale": "en-US"
}
Note
The text field for inbound messages sometimes contains mentions. Be sure to properly check and strip those. For more information, see Mentions.
Teams channel data
The channelData
object contains Teams-specific information and is the definitive source for team and channel IDs. You should cache and use these IDs as keys for local storage.
A typical channelData object in an activity sent to your bot contains the following information:
eventType
Teams event type; passed only in cases of channel modification events.tenant.id
Microsoft Entra tenant ID; passed in all contexts.team
Passed only in channel contexts, not in personal chat.id
GUID for the channel.name
Name of the team; passed only in cases of team rename events.
channel
Passed only in channel contexts when the bot is mentioned or for events in channels in teams where the bot has been added.id
GUID for the channel.name
Channel name; passed only in cases of channel modification events.
channelData.teamsTeamId
Deprecated. This property is included only for backwards compatibility.channelData.teamsChannelId
Deprecated. This property is included only for backwards compatibility.
Example channelData object (channelCreated event)
"channelData": {
"eventType": "channelCreated",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
},
"channel": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
"name": "My New Channel"
},
"team": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
}
}
.NET example
The Microsoft.Bot.Connector.Teams NuGet package provides a specialized TeamsChannelData
object, which exposes properties to access Teams-specific information.
TeamsChannelData channelData = activity.GetChannelData<TeamsChannelData>();
string tenantId = channelData.Tenant.Id;
Sending replies to messages
To reply to an existing message, call ReplyToActivity
in .NET or session.send
in Node.js. The Bot Builder SDK handles all the details.
If you choose to use the REST API, you can also call the /v3/conversations/{conversationId}/activities/{activityId}
endpoint.
The message content itself can contain simple text or some of the Bot Framework supplied cards and card actions.
Please note that in your outbound schema you should always use the same serviceUrl
as the one you received. Be aware that the value of serviceUrl
tends to be stable but can change. When a new message arrives, your bot should verify its stored value of serviceUrl
.
Updating messages
Rather than have your messages be static snapshots of data, your bot can dynamically update messages inline after sending them. You can use dynamic message updates for scenarios such as poll updates, modifying available actions after a button press, or any other asynchronous state change.
The new message need not match the original in type. For instance, if the original message contained an attachment, the new message can be a text message.
Note
You can update only content sent in single-attachment messages and carousel layouts. Posting updates to messages with multiple attachments in list layout is not supported.
REST API
To issue a message update, perform a PUT request against the /v3/conversations/<conversationId>/activities/<activityId>/
endpoint using a given activity ID. To complete this scenario, you should cache the activity ID returned by the original POST call.
PUT /v3/conversations/19%3Aja0cu120i1jod12j%40skype.net/activities/012ujdo0128
{
"type": "message",
"text": "This message has been updated"
}
.NET example
You can use the UpdateActivityAsync
method in the Bot Builder SDK to update an existing message.
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity reply = activity.CreateReply($"You sent {activity.Text} which was {activity.Text.Length} characters");
var msgToUpdate = await connector.Conversations.ReplyToActivityAsync(reply);
Activity updatedReply = activity.CreateReply($"This is an updated message");
await connector.Conversations.UpdateActivityAsync(reply.Conversation.Id, msgToUpdate.Id, updatedReply);
}
}
Node.js example
You can use the session.connector.update
method in the Bot Builder SDK to update an existing message.
function sendCardUpdate(bot, session, originalMessage, address) {
var origAttachment = originalMessage.data.attachments[0];
origAttachment.content.subtitle = 'Assigned to Larry Jin';
var updatedMsg = new builder.Message()
.address(address)
.textFormat(builder.TextFormat.markdown)
.addAttachment(origAttachment)
.toMessage();
session.connector.update(updatedMsg, function(err, addresses) {
if (err) {
console.log(`Could not update the message`);
}
});
}
Starting a conversation (proactive messaging)
You can create a personal conversation with a user or start a new reply chain in a channel for your team bot. This lets you message your user or users without having them first initiate contact with your bot. For more information, see the following articles:
For more information on conversations started by bots, see proactive messaging for bots.
Deleting messages
Messages can be deleted using the connector.delete()
method in the BotBuilder SDK.
bot.dialog('BotDeleteMessage', function (session: builder.Session) {
var msg = new teams.TeamsMessage(session).text("Bot will delete this message in 5 sec.")
bot.send(msg, function (err, response) {
if (err) {
console.log(err);
session.endDialog();
}
console.log('Proactive message response:');
console.log(response);
console.log('---------------------------------------------------')
setTimeout(function () {
var activityId: string = null;
var messageAddress: builder.IChatConnectorAddress = null;
if (response[0]){
messageAddress = response[0];
activityId = messageAddress.id;
}
if (activityId == null)
{
console.log('Message failed to send.');
session.endDialog();
return;
}
// Bot delete message
let address: builder.IChatConnectorAddress = {
channelId: 'msteams',
user: messageAddress.user,
bot: messageAddress.bot,
id : activityId,
serviceUrl : (<builder.IChatConnectorAddress>session.message.address).serviceUrl,
conversation: {
id: session.message.address.conversation.id
}
};
connector.delete(address, function (err) {
if (err)
{
console.log(err);
}
else
{
console.log("Message: " + activityId + " deleted successfully.");
}
// Try editing deleted message would fail
var newMsg = new builder.Message().address(address).text("To edit message.");
connector.update(newMsg.toMessage(), function (err, address) {
if (err)
{
console.log(err);
console.log('Deleted message can not be edited.');
}
else
{
console.log("There is something wrong. Message: " + activityId + " edited successfully.");
console.log(address);
}
session.endDialog();
});
});
}, 5000);
});
})