I am (on external events) posting messages to my microsoft team channel.
var activity = new Activity("message", text: $"", attachments: new List<Microsoft.Bot.Schema.Attachment>
{
new Microsoft.Bot.Schema.Attachment{ ContentType = "application/vnd.microsoft.card.adaptive" , Content= new JRaw(json)}
});
var a = await connector.Conversations.SendToConversationAsync("hardcoded channel string, cant find it anywhere",
activity);
Which works, the adaptive card is shown in my channel.
I now want to follow up with an reply to this activity later.
var b = await connector.Conversations.ReplyToActivityAsync("same channel id hardcoded", a.Id, new Activity("message", text: "user disconected",replyToId : a.Id));
but the message is not shown as a reply but instead just a new message in the channel.
The id returned from response of SendToConversationAsync, do not look like an activity id, so how do i get the activity id such i can reply to it properly.
The connectorClient is created like:
var connector = new ConnectorClient(new Uri("https://smba.trafficmanager.net/emea/"), appCredentials);
Have a look at this sample to see how to send proactive messages in BotBuilder V4: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages
Of particular interest should be the CompleteJobAsync method:
// Sends a proactive message to the user.
private async Task CompleteJobAsync(
BotAdapter adapter,
string botId,
JobLog.JobData jobInfo,
CancellationToken cancellationToken = default(CancellationToken))
{
await adapter.ContinueConversationAsync(botId, jobInfo.Conversation, CreateCallback(jobInfo), cancellationToken);
}
From this we can see that the trick is to use BotAdapter.ContinueConversationAsync
I've tested this in Teams and found that it will live up to its name by continuing the conversation rather than starting a new one.
EDIT: I now understand that you meant to ask something more like this question: Microsoft Bot Connector - get activity id from response after sending message
That is to say, you understand that in a Microsoft Teams team channel, all activities will share the same "root" conversation ID that looks like this: 19:[email protected]
And each "thread" will have its own conversation ID that consists of the root ID plus the message ID that started the thread: 19:[email protected];messageid=1545970892795
So if you want to start a new thread then you can send a message to the root conversation ID and if you want to reply to a thread then you can send a message to the thread's conversation ID. In this regard, there's no difference between SendToConversationAsync
and ReplyToActivityAsync
because, despite appearances, Teams doesn't "nest" messages under other messages. Instead, each message chain is identified by its own conversation ID.
So in order to send a message to a thread, you've discovered that you need to know the ID of the message that started the thread. Unfortunately, the ID returned by SendToConversationAsync
and ReplyToActivityAsync
is in a different form from the one you need. After extensive testing I've discovered that Teams seems to have two forms of message ID:
So are you out of luck? Not at all! It turns out that if you create the thread with CreateConversationAsync
then it will return the thread's conversation ID which you can use for your replies. Here is an extension class to make it easy for you:
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Connector.Teams.Models;
using Microsoft.Bot.Schema;
namespace Microsoft.Bot.Connector.Teams
{
public static class TeamsExtensions
{
public static async Task<ConversationResourceResponse> CreateTeamsThreadAsync(
this ITurnContext turnContext,
Activity activity,
CancellationToken cancellationToken = default(CancellationToken))
{
var connectorClient = turnContext.TurnState.Get<IConnectorClient>() as ConnectorClient;
activity.ChannelData = turnContext.Activity.ChannelData;
return await connectorClient.Conversations.CreateTeamsThreadAsync(turnContext.Activity.Conversation.Id, activity, cancellationToken);
}
public static async Task<ConversationResourceResponse> CreateTeamsThreadAsync(
this IConversations conversations,
string conversationId,
Activity activity,
CancellationToken cancellationToken = default(CancellationToken))
{
var channelData = activity.GetChannelData<TeamsChannelData>();
var parameters = new ConversationParameters
{
ChannelData = new TeamsChannelData
{
Channel = channelData.Channel,
Team = channelData.Team,
Tenant = channelData.Tenant,
},
Activity = activity,
};
return await conversations.CreateConversationAsync(parameters, cancellationToken);
}
}
}
Since I saw that you already have a ConnectorClient
and a conversation ID I made a method that uses those. You can also call the method that extends ITurnContext
if you're want to create a thread in response to a message from a user. All you have to do is save the Id
value of the ConversationResourceResponse
that the method returns so you can use it for your proactive messages.