In our Teams calling bot, we would like to transfer certain calls to specific Teams users, PSTN, but also to an other Teams calling bot and/or voicemail.
For specific Teams users and PSTN we got it working. If we want to transfer a call to another application, we can do so by using its pstn number. But ideally we would also like to transfer using its objectId.
I tried using a transferrequest like this:
var requestBody = new CallTransferRequestBody()
{
TransferTarget = new InvitationParticipantInfo()
{
Identity = new IdentitySet()
{
AdditionalData = new Dictionary<string, object>()
}
}
};
requestBody.TransferTarget.Identity.Application = new Identity { Id = transferTargetId };
//this line does not make any difference
requestBody.TransferTarget.Identity.Application.SetTenantId(tenantId);
But this results in a "Request authorization tenant mismatch." error. Is it possible to directly transfer to another application?
I haven't tried voicemail boxes yet, but if any info on how to transfer to those, is appreciated.
Basically we can transfer an active peer-to-peer call. This is only supported if both the transferee and transfer target are Microsoft Teams users that belong to the same tenant.
However for redirecting call to call queue or auto attendants, you can use the "applicationInstance" identity. The bot is expected to redirect the call before the call times out. The current timeout value is 15 seconds.
const requestBody = {
"targets": [{
"#odata.type": "#microsoft.graph.invitationParticipantInfo",
"identity": {
"#odata.type": "#microsoft.graph.identitySet",
"applicationInstance": {
"#odata.type": "#microsoft.graph.identity",
"displayName": "Call Queue",
"id": queueId
}
}
}],}
Please refer to the documentation here: https://learn.microsoft.com/en-us/graph/api/call-redirect?view=graph-rest-beta&tabs=csharp#request
The redirect API is still having that limitation from my understanding.
But that should work with the new Transfer API:
https://learn.microsoft.com/en-us/graph/api/call-transfer?view=graph-rest-beta&tabs=http
Related
We had some code that has been working for the past 10 months (since it was developed) and just stopped working this afternoon. It's a WebAPI code to send a channel message mentioning the bot and a user, which now is returning "Bad Request. Invalid request body was sent."
If the "Mentions" property is not provided, the call works, and the message is sent without the #mentions. So, I wonder if there was a breaking change in this API that's now expecting a different format for the "Mentions" property.
It's quite simple to reproduce by following the example code found in the Microsoft Graph documentation.
I'm posting here in the hope some fellow dev spots something obvious or is aware of an alternative way of using the API that it might stop complaining, as Microsoft takes forever to reply.
Here's the code we have that can lead me to discover the issue:
private async Task SendMentionToTheBotAsync(GraphServiceClient onBehalfOfClient, string userName, string teamId, string channelId)
{
var supportAgentUser = await onBehalfOfClient.Me.Request().GetAsync();
var chatMessage = new ChatMessage
{
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = $"<at id=\"0\">{Configuration["BotName"]}</at>: This is the start of the conversation between {userName} and <at id=\"1\">{supportAgentUser.DisplayName}</at>."
},
Mentions = new List<ChatMessageMention>
{
new ChatMessageMention
{
Id = 0,
MentionText = Configuration["BotName"],
Mentioned = new IdentitySet
{
Application = new Identity
{
DisplayName = Configuration["BotName"],
Id = Configuration["BotAppId"],
AdditionalData = new Dictionary<string,object>
{
{
"applicationIdentityType", "bot"
}
}
}
}
},
new ChatMessageMention
{
Id = 1,
MentionText = supportAgentUser.DisplayName,
Mentioned = new IdentitySet
{
User = new Identity
{
DisplayName = supportAgentUser.DisplayName,
Id = supportAgentUser.Id,
AdditionalData = new Dictionary<string,object>
{
{
"userIdentityType", "aadUser"
}
}
}
}
}
}
};
await onBehalfOfClient.Teams[teamId].Channels[channelId].Messages
.Request()
.AddAsync(chatMessage);
}
Microsoft Support responded with :
"Thank you for contacting Microsoft Support.
I understand the issue is related to the post messages to Teams. Based on the screenshot, it seems you are using mention to a channel. It's possible that you are using key "conversationIdentityType#odata.type" in your request.
Could you please try to remove "conversationIdentityType#odata.type" key from the request body and try again. It should work. It is because deployment is on the way in the Asia region. Once it's 100% rolled out, this key WILL NOT be entertained in the request."
Removed the key and it worked for me.
Paulo,
Unfortunately i am not a programmer. I am using Graph calls in a Microsoft 365 Power Automate workflow. I have an app that i use to get the Authorisation Bearer token and then post to Teams messages using a graph HTTP action.
Here is the syntax of the HTTP ( purple items are variables if u r not familiar with Flow )
click to view image of Power Automate workflow HTTP action
I have created a Teams bot in .NET Core from following the sample found here: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/57.teams-conversation-bot
This is working and is running locally with ngrok. I have a controller with a route of api/messages:
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter Adapter;
private readonly IBot Bot;
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
Adapter = adapter;
Bot = bot;
}
[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await Adapter.ProcessAsync(Request, Response, Bot);
}
}
I now want to call a POST to api/messages from my Angular client using TypeScript to send a proactive message to a specific Teams user.
I did figure out how to set the ConversationParameters in TeamsConversationBot.cs to a specific Teams user by doing the following:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("[insert unique Teams user guid here]") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
but what I'm struggling with is how to build a JSON request that sends the Teams user guid (and maybe a couple other details) to my api/messages route from TypeScript.
How do I go about doing this? What parameters/body do I need to send? I haven't been able to find samples online that show how to do this.
Update below for added clarification
I am building a web chat app using Angular for our customers. What I'm trying to do is send a proactive message to our internal employees, who are using Microsoft Teams, when a customer performs some action via the chat app (initiates a conversation, sends a message, etc.).
I've built a Teams bot using .NET Core using this sample: https://kutt.it/ZCftjJ. Modifiying that sample, I can hardcode my Teams user ID and the proactive message is showing up successfully in Teams:
var proactiveMessage = MessageFactory.Text($"This is a proactive message.");
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("insert Teams ID here") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(teamsChannelId, serviceUrl, credentials, conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(_appId, conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
What I'm struggling with is:
How to configure my Angular app to notify my bot of a new proactive message I want to send.
How to configure the bot to accept some custom parameters (Teams user ID, message).
It sounds like you've got some progress with pro-active messaging already. Is it working 100%? If not, I've covered the topic a few times here on stack overflow - here's an example that might help: Programmatically sending a message to a bot in Microsoft Teams
However, with regards -trigging- the pro-active message, the truth is you can do it from anywhere/in any way. For instance, I have Azure Functions that run on their own schedules, and pro-active send messages as if they're from the bot, even though the code isn't running inside the bot at all. You haven't fully described where the Angular app fits into the picture (like who's using it for what), but as an example in your scenario, you could create another endpoint inside your bot controller, and do the work inside there directly (e.g. add something like below:)
[HttpPost]
public async Task ProActiveMessage([FromQuery]string conversationId)
{
//retrieve conversation details by id from storage (e.g. database)
//send pro-active message
//respond with something back to the Angular client
}
hope that helps,
Hilton's answer is still good, but the part about proactively messaging them without prior interaction requires too long of a response. So, responding to your latest comments:
Yes, the bot needs to be installed for whatever team the user resides in that you want to proactively message. It won't have permissions to do so, otherwise.
You don't need to override OnMembersAddedAsync; just query the roster (see below).
You don't need a conversation ID to do this. I'd make your API, instead, accept their Teams ID. You can get this by querying the Teams Roster, which you'll need to do in advance and store in a hash table or something...maybe a database if your team size is sufficiently large.
As far as required information, you need enough to build the ConversationParameters:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new ChannelAccount[] { teamMember },
TenantId = turnContext.Activity.Conversation.TenantId,
};
...which you then use to CreateConversationAsync:
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(
teamsChannelId,
serviceUrl,
credentials,
conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(
_appId,
conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
Yes, you can modify that sample. It returns a Bad Request because only a particular schema is allowed on /api/messages. You'll need to add your own endpoint. Here's an example of NotifyController, which one of our other samples uses. You can see that it accepts GET requests. You'd just need to modify that our build your own that accepts POST requests.
All of this being said, all of this seems like it may be a bigger task than you're ready for. Nothing wrong with that; that's how we learn. Instead of jumping straight into this, I'd start with:
Get the Proactive Sample working and dig through the code until you really understand how the API part works.
Get the Teams Sample working, then try to make it message individual users.
Then build your bot that messages users without prior interaction.
If you run into trouble feel free to browse my answers. I've answered similar questions to this, a lot. Be aware, however, that we've switched from the Teams Middleware that I mention in some of my answers to something more integrated into the SDK. Our Teams Samples (samples 50-60) show how to do just about everything.
I've got a Bot Framework V3 bot code base that is working in a half dozen or so different customer Teams tenants, and on our internal Teams tenant without issues.
In one particular customer tenant, attempts to create a proactive message to a Teams Channel are failing with a ConversationNotFound 404 error, when I call ConnectorClient.Conversations.CreateConversationAsync().
My code to create the conversation and post an activity in the channel looks like this:
var teamsChannelId = "19:deadbeef1234#thread.skype"; // insert the real channel ID obtained from lookups against Graph API...
var botCredentials = new MicrosoftAppCredentials(/* Bot ID & password */);
MicrosoftAppCredentials.TrustServiceUrl("https://smba.trafficmanager.net/amer/", DateTime.MaxValue);
using (var connectorClient = new ConnectorClient(new Uri("https://smba.trafficmanager.net/amer/"), botCredentials)) {
var botId = new ChannelAccount("28:" + botCredentials.MicrosoftAppId);
var msg = Activity.CreateMessageActivity();
msg.From = botId;
var card = MakeCard(); // builds an AdaptiveCard...
msg.Attachments.Add(new Attachment(AdaptiveCard.ContentType, content: card));
var parameters = new ConversationParameters() {
Bot = botId,
ChannelData = new TeamsChannelData() {
Channel = new ChannelInfo(teamsChannelId)
},
Activity = (Activity)msg
};
// This throws an Microsoft.Bot.Connector.ErrorResponseException with the code "ConversationNotFound"
ConversationResourceResponse convoResponse = await connectorClient .Conversations.CreateConversationAsync(parameters);
}
As I mentioned initially, this code may not be perfect, but it is working on a number of different Teams and Azure environments, but failing in this particular environment. The HTTP response from Bot Framework looks like this:
"Response": {
"StatusCode": 404,
"ReasonPhrase": "Not Found",
"Content": "{\"error\":{\"code\":\"ConversationNotFound\",\"message\":\"Conversation not found.\"}}",
"Headers": {
"Date": [
"Wed, 04 Sep 2019 14:43:24 GMT"
],
"Server": [
"Microsoft-HTTPAPI/2.0"
],
"Content-Length": [
"77"
],
"Content-Type": [
"application/json; charset=utf-8"
]
}
Stack Trace:
Microsoft.Bot.Connector.ErrorResponseException: Operation returned an invalid status code 'NotFound'
at Microsoft.Bot.Connector.Conversations.<CreateConversationWithHttpMessagesAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Bot.Connector.ConversationsExtensions.<CreateConversationAsync>d__3.MoveNext()
The bot is able to handle incoming 1-1 chat conversations without issue over webchat, directline and the Teams connectors, so I don't think there are any issues with the bot credentials, or the bot registration configuration.
The bot has been added as an app for Microsoft Teams, uploaded to the tenant, and added to the appropriate Team.
I've explored the possibility that the region of the Bot Framework registration in Azure might be causing an issue, but I've reproduced the client's configuration on our end, and can't reproduce the problem.
Any suggestions would be very welcome.
I have a feeling your parameters is missing the Tenant. This may explain why it fails on some tenants and not others. Try something like this:
var parameters = new ConversationParameters
{
Members = new[] { new ChannelAccount(userId) },
ChannelData = new TeamsChannelData
{
Tenant = new TenantInfo(activity.Conversation.TenantId),
},
};
#Trinetra-MSFT is also correct. You should not hard-code the service URL; some of your users may be outside /amer.
Although possible to some extent, "Proactive Messaging" shouldn't be thought of as "messaging users who have not spoken with the bot", so much as "messaging a user about something not related to a previous conversation". Generally speaking, proactive messaging needs to be done by saving a conversation reference from a user that the bot has had a past conversation with. This is how the Bot Framework, specifically, defines Proactive Messaging.
For Teams, per Proactive Messaging for Bots:
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, groupChat 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.
See this SO answer for additional help. Note: it's written for a V4 bot, so you may need to make some adjustments.
Let me know if you run into issues and I will adjust my answer accordingly.
I am using code based on https://github.com/Microsoft/BotFramework-WebChat/blob/master/samples/15.d.backchannel-send-welcome-event/index.html
When I load the web page I get two of the welcome messages. Looking at the console output of my bot I can see two conversation updates happening.
This doesn't happen with the Bot framework emulator, which only shows one welcome message.
The only place where my code differs from the sample is in rendering:
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
styleOptions,
userID: guid(),
}, document.getElementById('webchat'));
Why is this hapening? Why is the web channel sending two "join" events for the user?
My code handling conversation updates looks like this:
} else if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
if (DEBUG) { console.log("ConversationUpdate"); }
// Do we have any new members added to the conversation?
if (turnContext.activity.membersAdded.length !== 0) {
// Iterate over all new members added to the conversation
for (var idx in turnContext.activity.membersAdded) {
console.log(turnContext.activity.membersAdded);
// Greet anyone that was not the target (recipient) of this message
// the 'bot' is the recipient for events from the channel,
// turnContext.activity.membersAdded == turnContext.activity.recipient.Id indicates the
// bot was added to the conversation.
if (turnContext.activity.membersAdded[idx].id != turnContext.activity.recipient.id) {
if (DEBUG) {console.log("Starting MASTER_DIALOG");}
const user = await this.userProfile.get(turnContext, {});
user.id = this.guid();
await this.userProfile.set(turnContext, user);
await this.userState.saveChanges(turnContext);
return await dialogContext.beginDialog(MASTER_DIALOG)
}
}
}
}
Using the ConversationUpdate event for sending a welcome message is not recommended. Read more about how to properly send a greeting message.
There will be two ConversationUpdate events per connection. One for when bot joins the conversation and one for when a (human) user joins the conversation. In your current code you are iterating over all new members, where you have to filter out the bot itself.
A better option would be to make use of a custom event sent using the backchannel. In the example you mention, you already have this functionality. It will sent a new event webchat/join to your bot, which even includes the browser language by default.
So my main objective is to update network user's outlook calendar from sql server data using office365 api every few minutes. I am stuck at how to get access for other user's outlook calendar? Looked at below link but didnt asnwser much...do i need azure subscription in order to do this? If someone can point me to right direction, that would be great
https://msdn.microsoft.com/en-us/office/office365/howto/common-app-authentication-tasks
I am stuck at how to get access for other user's outlook calendar?
In this case, you can consider using the application permission.
In Azure AD:
register a Web Application in your Azure AD.
add “Read and write calendars in all mail boxes” permission
generate the application secret key
In your application, call Office 365 Graph API - create events by using application token.
http://graph.microsoft.io/en-us/docs/api-reference/v1.0/api/user_post_events
var tmgr = new ApplicationTokenManagement();
var token = tmgr.AcquireToken(Settings.ResourceUrlOfGraph);
var api = new Graph.GraphCalendarAPI(token);
JObject body = new JObject
{
{"subject", "Create from Office 365 API"},
{"start", new JObject { { "DateTime", "2016-03-09T00:00:00"}, { "TimeZone", "China Standard Time" } } },
{"end", new JObject { { "DateTime", "2016-03-10T00:00:00"}, { "TimeZone", "China Standard Time" } } },
{"isAllDay", true }
};
var task = api.CreateEventAsync(body, "user#youcompany.com");
task.Wait();
You can find the complete sample here.