Is it possible to scope a Teams Messaging Extension? - botframework

I developed a messaging extension for Teams. I want it to only be available for the teams i specifically install the app to. Is that possible and how? I can't find any info on this, but my use-case does not seem to be far-fetched, so i would expect it to be possible.
use-case: Members of a team use an external system to register cases. I want them to be able to search and reference cases within the teams corresponding project site in the external system. I add a tab to the general channel that refers to the project site, and the messaging extension uses the contentUrl of that tab to query the right case list. I don't want the extension to be available outside the teams channels. By default it seems that the extension is available in every chat input option.

Message extensions do no have a scope defined and they are available once you install it in teams. Currently, it is no possible to restrict to show the message extension in one team

I ended up responding with a card that handles the error:
private MessagingExtensionResponse errorResponse(string title, string errorText)
{
MessagingExtensionResult composeExtensionResult = new MessagingExtensionResult
{
Type = "result",
AttachmentLayout = "list",
Attachments = new List<MessagingExtensionAttachment>(),
};
ThumbnailCard h = new ThumbnailCard()
{
Title = title,
Text = errorText,
};
composeExtensionResult.Attachments.Add(h.ToAttachment().ToMessagingExtensionAttachment());
var messagingExtensionResponse = new MessagingExtensionResponse();
messagingExtensionResponse.ComposeExtension = composeExtensionResult;
return messagingExtensionResponse;
}
Although it would be nice to be able to scope the extension, this way i can catch some more invalid usage. For example:
var currentTeam = new TeamDetails();
IList<ChannelInfo> currentTeamChannels = new List<ChannelInfo>();
try
{
currentTeam = await TeamsInfo.GetTeamDetailsAsync(turnContext, turnContext.Activity.TeamsGetTeamInfo().Id, cancellationToken);
currentTeamChannels = await TeamsInfo.GetTeamChannelsAsync(turnContext, turnContext.Activity.TeamsGetTeamInfo().Id, cancellationToken);
}
catch
{
return errorResponse("Permission error", "This app has no permissions to this team / channel. Please add the app to this team / channel.");
}
Got the suggestion from this question: Is it possible for a teams messaging extension to return a plaintext response instead of a card?

Related

Calling my .NET Core Teams Bot from Angular

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.

Is it possible to launch native apps using Microsoft bot framework?

I am creating a Cortana skill on the Cortana canvas, I have a button.
I wanted to know if it possible to have an 'imback' type of button to open a webpage.
Ye, for example
var message = context.MakeMessage() as IMessageActivity;
message.ChannelData = JObject.FromObject(new
{
action = new { type = "LaunchUri", uri = "skype:echo123?call" }
});
await context.PostAsync(message);
this code will start a call with echo123 user on skype
Reference: https://learn.microsoft.com/en-us/cortana/tutorials/bot-skills/bot-entity-channel-data
You can supply an openUrl to a card action, or even use ChannelData to send a LaunchUri command, deep linking to an application. (I haven't tried this, but I assume 'http://websitename.com' will launch in the Cortana host platform's default browser.)
activity.ChannelData = new {
action = new { type = "LaunchUri", uri = "http://websitename.com"}
};

How to add an Attachment to Outlook Mail from UWP App programmatically?

I am developing an UWP Application , i want to add a Attachment to outlook from UWP app programmatically
Request you to please me know if any alternatives are there.
Looking forward for your response.
You can use the share contract to send some data to the compliant applications (including outlook). It allows you to share some text and data with any compliant apps.
To activate the sharing, you just need to register to the DataRequested event and show the share UI:
DataTransferManager.GetForCurrentView().DataRequested += OnDataRequested;
DataTransferManager.ShowShareUI();
Then, in the event handler:
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
try
{
args.Request.Data.Properties.Title = "Share Title"
args.Request.Data.Properties.Description = "Share some data/file";
var file = await ApplicationData.Current.TemporaryFolder.GetFileAsync("myFileToShare.xxx");
args.Request.Data.SetStorageItems(new IStorageItem[] { logFile });
}
catch
{
args.Request.FailWithDisplayText("Unable to share data");
}
finally
{
deferral.Complete();
sender.DataRequested -= OnDataRequested;
}
}
Once done, the system will show the share UI where the user will be able to select the app he want. This app will receive the sent data.
While #Vincent's answer is perfect when you want to use Share Contract, if you want to use Just Email and attach the File, Below is a simple Method that i use in one of my App.
internal async void ShowEmail(string body, string subject, StorageFile attachment)
{
EmailMessage email = new EmailMessage();
email.Subject = subject;
email.Body = body;
var stream = RandomAccessStreamReference.CreateFromFile(attachment);
email.SetBodyStream(EmailMessageBodyKind.Html, stream);
await EmailManager.ShowComposeNewEmailAsync(email);
}
Above method is a strip down of the example from Here

Authorization needed for classroom.profile.emails

I'm working on a web app in Google Apps Script, and I'm having some trouble understanding how the authorization is handled. When accessing the web app as the user using the app, it prompts for authorization, and everything appears okay. However, I'm call userProfiles.get and looking for student email addresses, and it returns the profile without the email.
function classRosters() {
var teacher = Classroom.UserProfiles.get(Session.getActiveUser().getEmail());
var classList = Classroom.Courses.list({teacherId: teacher.id}).courses;
var classes = [];
for (i in classList) {
if (classList[i].courseState != 'ACTIVE') {
continue;
}
var class = classList[i];
var classId = classList[i].id;
var className = classList[i].name;
classes.push([className]);
var teacherId = Classroom.Courses.Teachers.get(classId, classList[i].ownerId).userId;
var teacherEmail = Classroom.UserProfiles.get(teacherId);
var title = Classroom.Courses.get(classId).name;
var students = Classroom.Courses.Students.list(classId).students;
var studentArray = [];
if (students) {
for (j in students) {
var currStudent = students[j];
var email = Classroom.UserProfiles.get(currStudent.userId).emailAddress;
var email = Classroom.Courses.Students.get(classId, currStudent.userId).profile.emailAddress;
studentArray.push(email);
Logger.log(email);
}
}
for (j in classes) {
if (className.indexOf(classes[j]) > -1) {
var classIndex = +j;
classes[classIndex].push(studentArray);
}
}
}
return classes;
}
I've played with the API explorer, and it shows that classroom.profile.email is required, but that's not included in the scopes. When I use the API explorer, I can authorize, and it works, and my web app will work as well until the authorization from the explorer expires.
Is there any method to prompt for authorization in the GAS library for the Classroom advanced service? I can't find anything much that's specific to GAS and not part of the overall API.
Thanks,
James
Unfortunately Apps Script doesn't allow you to request additional scopes for your advanced services. The email and photos scopes aren't required to execute the method, but are required to return email and photo data in the response. You can follow issue 3070 for progress on this problem.
Update 2015-08-17:
We just implemented a workaround, which is that the Classroom advanced service now always prompts for the following fixed set of scopes:
https://www.googleapis.com/auth/classroom.courses
https://www.googleapis.com/auth/classroom.rosters
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
This provides access to emails, but does mean that the scopes requested for a given script may be more than it actually needs. We hope this unblocks admins that are trying to use Apps Script to manage their Classroom data, while we work on a longer-term solution for dealing with optional scopes in Apps Script.

Node js - Creating persistent private chat rooms

I've been reading so much a bout node js lately, and the chat capabilities seem very nice. However, the only chat examples I've seen basically broadcast a chat server to a fixed URL (like a meeting room). Is it possible to use node js in part to create a chat client more like gchat? - where a chat window is popped up on the current page and then persists through multiple pages. Has anyone seen an example of this yet?
If not, suggestions for other technologies to use for this purpose (I know that's been answered in other questions)?
Thanks.
I'll give you a pseudo implementation relying on jquery and now to abstract away tedious IO and tedious DOM manipulation from the solution.
// Server
var nowjs = require('now');
var everyone = nowjs.initialize(httpServer);
everyone.now.joinRoom = function(room) {
nowjs.getGroup(room).addUser(this.user.clientId);
}
everyone.now.leaveRoom = function(room) {
nowjs.getGroup(room).removeUser(this.user.clientId);
}
everyone.now.messageRoom = function(room, message) {
nowjs.getGroup(room).now.message(message);
}
// Client
var currRoom = "";
$(".join").click(function() {
currRoom = ...
now.joinRoom(currRoom);
});
$(".send").click(function() {
var input = ...
now.messageRoom(currRoom, input.text());
});
now.messageRoom = function(message) {
$("messages").append($("<div></div>").text(message));
};
I only just noticed myself that the new version of nowjs (0.5) has the group system in build. This basically does what you want for you. No hassle.
If you want you can remove the nowjs dependency and replace it with 100/200 lines of code. I'll leave that as an exercise for the user.
Take a look at AjaxIM: https://github.com/freq32/AjaxIM
This is a facebook-style chat application (think friends list, small persistent chat bar at the bottom of the screen, popup chats) based on nodejs.

Resources