I am developing a bot for Microsoft Teams using the Bot Framework SDK v4 for NodeJS. Is there a way that the bot can automatically initiate a conversation in a channel, rather than user initiating the conversation? My bot works fine when the user initiates the conversation. Any suggestions on how I can proceed with this?
MS Teams calls that a "Proactive Message" (note: Bot Framework generally defines a "proactive message" as sending a user a message not related to current conversation, that you have a reference for. Teams lumps a few things into this category). You can read more about how to use proactive messaging from the official Teams docs. Or, more specifically, creating a channel conversation.
The gist of it is that you need to capture a conversationUpdate and check for a new member added to the conversation or fetch the team roster, then you send the proactive message.
Note: For MS Teams, the user or team will have to add the bot first:
Bots can create new conversations with an individual Microsoft Teams user as long as your bot has user information obtained through previous addition in a personal or team scope. This information enables your bot to proactively notify them. For instance, if your bot was added to a team, it could query the team roster and send users individual messages in personal chats, or a user could #mention another user to trigger the bot to send that user a direct message.
Some developers come across 401: Unauthorized errors when using proactive messaging, especially if the bot was restarted for some reason and the bot is attempting to re-initiate a proactive message. You can read more about preventing that by using trustServiceUrl from this Sample (this is my branch, which is being used to submit a Pull Request to update the Proactive Sample with trustServiceUrl info).
You can initiate a brand new conversation using the connector Client in the Botframework V4 and the Teams Extensions V4. In nodejs, you will find a solution in one of the comments for this Github Issue. For anyone looking for a solution in C#, here is a detailed blog post about accomplishing this in C# version of the botframework.
in nodejs :
var conversationReference = TurnContext.getConversationReference(context.activity)
connectorClient = await createConnectorClient(context)
var conversationParameters = {
isGroup: true,
bot: conversationReference.bot,
channelData: (await teamsCtx.getTeamsChannelData()),
tenantId: teamsCtx.tenant.id,
activity: MessageFactory.text("Queue Summary Placeholder") as Activity
} as ConversationParameters
await connectorClient.conversations.createConversation(conversationParameters)
In C#
ConnectorClient _client = new ConnectorClient(new Uri(turnContext.Activity.ServiceUrl), await GetMicrosoftAppCredentialsAsync(turnContext), new HttpClient());
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
var conversationParameter = new ConversationParameters
{
Bot = turnContext.Activity.Recipient,
IsGroup = true,
ChannelData = channelData,
TenantId = channelData.Tenant.Id,
Activity = MessageFactory.Text(message)
};
var response = await _client.Conversations.CreateConversationAsync(conversationParameter);
Really we need to know when you want the bot to send the message, the bot framework TeamsActivityHandler class provides multiple methods that you can utilise for example:
onMembersAdded(BotHandler): Registers an activity event handler for the members added event, emitted for any incoming conversation update activity that includes members added to the conversation.
Learn more about the events / methods you can utilise here.
I ended up figuring it out and i wrote a bot controller that i can invoke on demand with the following code.
var conversationParameters = new ConversationParameters
{
IsGroup = true,
ChannelData = new TeamsChannelData
{
// this needs to come from the teams context.
Channel = new ChannelInfo(channelId),
},
Activity = (Activity)MessageFactory.Attachment(attachment)
};
// your service url may differ.
MicrosoftAppCredentials.TrustServiceUrl(String.IsNullOrEmpty(serviceUrl) ? constantServiceUrl : serviceUrl, DateTime.MaxValue);
var response = connectorClient.Conversations.CreateConversationAsync(conversationParameters).GetAwaiter().GetResult();
Related
I have created a Echo Bot in c# using QnA maker which is working absolutely fine now I wanted to achieve a scenario where if user ask any question and bot unable to find related answer than this question must be sent on Microsoft Teams channel where except will reply to the same and that message will sent to the user.
So, Is there any way to send message user message to Microsoft Teams for expert reply. If you have any sample code for the scenario please feel free to mention.
As per your current requirement this is kind of handoff or human live agent connect.
The following way you can achieve posting a message in ms team ( Go through this article Send proactive messages to Teams channels and users ).
Send proactive messages to Teams channels and users ( Microsoft Bot Framework v4 )
The user should be part of ms teams ( Azure AD valid users ).
Suggestions : If you are using domain bot then human live agent or handoff concept is the best approach otherwise you can integrate bin search api or any other third party api for unanswered question.
As per your requirement you can use Graph API to send message to channel using below code
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var chatMessage = new ChatMessage
{
Body = new ItemBody
{
Content = "Hello World"
}
};
await graphClient.Teams["{team-id}"].Channels["{channel-id}"].Messages
.Request()
.AddAsync(chatMessage);
Please go through this documentation for more info.
I want to call a messaging extension (with action command) and access Graph API via a bot for getting different resources of the channel (e.g. retrieve all messages or get replies for my message).
In the examples from Microsoft it is stated as a prerequisite that I have to do the "Bot channels registration" so that the access of the bot to the Graph API via OAuth2 works.
Do I really need this channel registration? Or is there another way?
As a test, I had created a azure free trial, with which I performed the "Bot channels registration" and could also save the ID and secret for the Graph Api access in the registration. With this I had success. Now the 30 days testing period is over and I'm interested in whether it would work without.
Thanks for your help
Update:
Thats my code to initialize graph api:
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(AppId)
.WithClientSecret(AppSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{Tenant}"))
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
For OAuth authentication, you need to register the bot with azure for sure.
I am attempting to send a message to a Teams Channel from within a proactive 3rd party WebHook callback that resides in my Teams Bot which is triggered externally some time after my bot conversation has ended.
My config.ServiceURL is 'https://smba.trafficmanager.net/amer/' which I got from my bot while conversing in a session.
My config.MicrosoftAppId value is the ApplicationID assigned to my bot App registration in Azure.
My config.MicrosoftAppPassword is a Client secret I created for the registered bot App.
My ChannelInfo value is the value I discovered by enumerating all the target team's channels in another application. I replaced the actual values with 'f's.
In Azure I added the following permission to my registered App: Delegate - Group.ReadWrite.All
When I attempt to create the conversation I get a Forbidden response. If I change my ChannelInfo below to a bogus value I receive a Not Found response as expected so at least the ChannelInfo appears to be a valid value.
Is there another permission I need to add? Have I missed step? Is there something I did wrong?
Here is the code:
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
var credentials = new MicrosoftAppCredentials(config.MicrosoftAppId, config.MicrosoftAppPassword);
if (!MicrosoftAppCredentials.IsTrustedServiceUrl(config.ServiceURL))
{
MicrosoftAppCredentials.TrustServiceUrl(config.ServiceURL);
}
var serviceUri = new Uri(config.ServiceURL);
var botConnector = new ConnectorClient(serviceUri, credentials);
Activity message = (Activity)Activity.CreateMessageActivity();
message.Text = "Hello World";
var conversationParameters = new ConversationParameters
{
Bot = message.From,
IsGroup = true,
ChannelData = new TeamsChannelData
{
Channel = new ChannelInfo("19:ffffffffffffffffffffffffffffffff#thread.skype")
},
Activity = (Activity)message
};
var conversationResponse = await botConnector.Conversations.CreateConversationAsync(conversationParameters);
await botConnector
.Conversations
.SendToConversationAsync(message);
Your code worked fine for me. Here's a few things you can try:
Ensure you sideloaded your bot into Teams via App Studio or manifest upload and that you're not just talking with the bot by appId
Ensure your bot is installed to the Team you want it to message
Add the domain your bot is hosted on to the Valid Domains section of the bot manifest
Esure you're sending the message to the correct conversation by changing .SendToConversationAsync(message); to .SendToConversationAsync(conversationResponse.Id, message);
Also note that the Microsoft.Bot.Builder.Teams package has been deprecated now that Bot<->Teams functionality has been rolled directly into the Bot Framework SDK. I highly recommend migrating away from it.
You may want to send proactive messages as shown in this sample. The other Teams Samples are numbers 50-60.
Let me know if you want to stick with that package and I can continue trying to provide support.
I am working on creating a chat bot using microsoft bot framework. Have a requirement to add an agent into the conversation if the bot is not able to identify customer's intent.
All the examples that I found are for botbuilder:3.7.0 but we are using botbuilder:4.1.5 .
Found a method in BotFrameworkAdapter, which would help us do this but it is not working.
reference = TurnContext.getConversationReference(context.activity);
await adapter.createConversation(reference, async (ctx) => {
console.log(ctx);
await ctx.sendActivity("Hi (in private) -- Successful ");
});
I am creating a bot with Microsoft Bot Framework that is supposed to, when receives notification from CI server, notify about build events participants of a particular chat group in Skype.
I don't quite get it, when I've added Skype bot to the chat, it has received an activity that presumably would have allowed me to save some Id at that stage. But since I need the bot to be proactive and post messages based on external stimuli, I would need to know the reference to that group chat permanently, including after re-deployment. But after redeployments, I don't have a conversation reference.
In theory, what bit of data, given that I save it during add time, would enable me to proactively send messages at any given point in time?
If it is ok that all participants "join" the conversation by writing first to the bot and if your bot accepts messages in similar Post method
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
string rawActivity = JsonConvert.SerializeObject(activity);
Save(rawActivity);
}
Then you are able to send messages to that conversation from your bot any time by using following code. You can restart or even redeploy your bot in the meantime. I have tested about one week as maximum time between consecutive messages.
public void MethodInvokedByExternalEvent(string externalMessage)
{
var activity = JsonConvert.DeserializeObject<Activity>(GetStoredActivity());
var replyActivity = activity.CreateReply(externalMessage);
ResourceResponse reply = null;
using (var client = new ConnectorClient(new Uri(activity.ServiceUrl)))
{
reply = client.Conversations.ReplyToActivity(replyActivity);
}
}