Monday, 24 February 2020

Microsoft Bot Framework v4: Send proactive messages to Teams channels and users

What is a Bot Framework Proactive message?

Usually, for starting a conversation with a Microsoft Teams bot, the user has to initiate the conversation either by sending a personal message to the bot, or by mentioning the bot in a Teams channel or by invoking a messaging extension.

With proactive messaging, the bot can start a conversation with a user (or in a Teams channel) without anyone having to invoke the bot first. This conversation can be started based on any custom logic fit for your application e.g. The occurrence of  an external event, or a webhook getting triggered or even on a scheduled periodical basis.

More about Bot Framework proactive messages here: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp

I should mention that the bot will first need to be installed in the Team, if you want to send a proactive message to a Teams channel, or to the users who are part of that team.



How to send proactive messages?

So in this post, let's look at a few code samples which make it very easy for our Teams Bot to send proactive messages to users or channels in a Team.

These code samples are based on a standalone .NET Core console app. This is mainly to show that as long as you have the necessary information, your code doesn't need to be running under the Bot messaging endpoint. Once you have the information from the Bot messaging endpoint, the proactive messaging code can run from any platform e.g. from an Azure Function.

If you have a look at the Bot Framework code samples published by Microsoft, they all use code which is running under the messaging endpoint. This initially led me to believe that even for proactive messaging, the code should live under the same endpoint. But as we will see in this post, that is not the case.

What are the prerequisites?

As mentioned before, our bot will need to be installed in a Team first. This will allow the bot messaging endpoint to receive the required values from Teams and send it to our proactive messaging code. If the bot is not installed in the Team, you will get a "Forbidden: The bot is not part of the conversation roster" error.

Base URL (serviceUrl)

This is the Url to which our proactive messaging code should send all the requests. This Url is sent by Teams in the Bot payload in the turnContext.Activity.serviceUrl property. For all intents and purposes this url will remain constant but after having a discussion with Microsoft, they have recommended that this url might change (very rarely) and our bot should have the logic for updating the stored base url periodically from the payload sent to the bot. More about the Base Url here: https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#base-uri

Internal Team Id

This is the internal team id which is not the same as the Office 365 Group Id. The internal team id is in the following format: 19:bf2b184e7cbb4f9f9ca1b47f755cd943@thread.skype

You can get the internal team id from the Bot payload in the channelData.team.id property. You can also get this id through the Microsoft Graph API: https://docs.microsoft.com/en-us/graph/api/resources/team?view=graph-rest-1.0#properties

Channel Id

If we want our bot to post to a specific channel in a Team, then we will need the channel id as well. The format for the channel id is exactly the same as the internal team id. Also, you can get the channel id from the bot payload as well as the Microsoft Graph api: https://docs.microsoft.com/en-us/graph/api/resources/channel?view=graph-rest-1.0#properties

Internal Teams User Id

This would only be needed if you want to send a proactive personal message to a specific user. For all users in a team, Teams maintains an encoded user id so that only bots installed in a team are able to message users. To get this user id, our bot needs to call the conversations/{conversationId}/members REST API endpoint. Fortunately for us the Bot Framework wraps this call in a handy SDK method as shown in the third code sample below.

So once we have all the required values from the Bot messaging endpoint, we are able to send proactive messages. For this sample code, I am using the Microsoft.Bot.Builder v4.7.2
https://www.nuget.org/packages/Microsoft.Bot.Builder/

1) Post a proactive message in a Teams channel



(click to zoom)

2) Post a proactive message in a Teams channel and mention a user in it



(click to zoom)


3) Post a proactive personal message to a user


(click to zoom)

Hope you found the post useful!

Wednesday, 5 February 2020

SPFx: Using React hooks to globally share service scope between components

In my previous posts, I have written quite a few times about SharePoint Framework service scopes (I will add links at the end of the article). In short, Service Scopes are the SPFx implementation of the Service Locator pattern i.e. a single shared "dictionary" where services (either oob SPFx or custom) are registered and can be consumed from any component in the application.

For example, without using service scopes, if we wanted to make a call to the Microsoft Graph (using MSGraphClient) from within a deeply nested react component, either we would have to pass in the SPFx context down all the components in the tree, or maybe a create a custom service which returns the web part context, and then call that service from within our nested component. Or maybe use redux to globally maintain the context in a single state object.

But with all these approaches (there may be more), testing the components would be difficult as they would have a dependency on the SPFx context which is hard to mock. Waldek Mastykarz has a great post on this.

Also, from a maintenance point of view, it could get tricky as almost all our components would start to depend on the entire context and we could easily loose track of which specific service from the context is needed by the component.

Now with my previous posts on service scopes, even though we were removing the dependency on the SPFx context, one issue still remained that the SPFx service scope was still needed to be passed into the component. We were just replacing the SPFx context with the SPFx service scopes. While this was good from a testing point of view, it wasn't great for maintainability.

Fortunately, in the recent versions of SPFx, React 16.8+ was supported which means that we can take advantage of React hooks. Specifically, the useContext hook. This gives us a very straightforward way to store the SPFx service scope in the global react context (which is different to the SPFx context) and then consume it from any component in our application no matter how deeply nested it is.

Let's see how to achieve this. In these code samples, I am using SPFx v1.10 which is the latest version at the time of writing.

1) The Application Context object


First, we need to create the React application context object which will be used to store and consume the service scope. For now I am only storing the serviceScope in the context. Other values can be stored here as well.


2) React Higher Order Component (HOC)


React hooks can only be used from functional components and not classes. With the SPFx generator creating classes by default and hooks being fairly new, I am sure there is a lot of code out there already which use classes and not functional components. Changing all code to use functional components instead of classes is a non-starter.

Fortunately, there is a way to use react hooks with classes by creating a Higher Order Component (HOC) which is a functional component. We can wrap all our class components with this HOC and safely consume the useContext hook from within this component.

(Update: If you are interested in going down the "full hooks" approach and doing away entirely with classes, Garry Trinder has got you covered. He has created a fork which only uses functional components and hooks so we don't need the HOC. If you want to take this approach, check out the code here: https://github.com/garrytrinder/spfx-servicescopes-hooks)


3) SPFx web part 


Next, we update our SPFx webpart to only pass in the serviceScope once to our top level component:


4) Top level React component 


Our top level component will need to be wrapped with the AppContext so that any nested component will be able to consume it. This just needs to be done once on the top level react component. You will notice that the HelloUser child component does not need any props passed in.


5) Child component  


Due to the Higher Order component and the useContext hook, we are able to access the serviceScope property from withing the child component. We can grab the MSGraphClient from the serviceScope and start making calls to the Graph:

And that's it! This way, we can use the React useContext hook to globally share our SPFx service scope.

Hopefully you have found this post helpful! All code is added in the GitHub repo here: https://github.com/vman/spfx-servicescopes-hooks


Also, if you are interested, here are all my previous articles on SPFx service scopes:

SharePoint Framework: Org Chart web part using Office UI Fabric, React, OData batching and Service scopes

Getting the current context (SPHttpClient, PageContext) in a SharePoint Framework Service

Service Locator pattern in SPFx: Using Service Scopes

Service Locator pattern in SPFx: Using nested scopes to work with multiple components

Wednesday, 15 January 2020

Create Microsoft Teams manifest manually for Personal app powered by SPFx

SPFx 1.10 was released recently which now includes support for Teams personal apps. Catch the announcement here: https://developer.microsoft.com/en-us/sharepoint/blogs/announcing-sharepoint-framework-1-10-extending-sharepoint-framework-across-microsoft-365/

To deploy a personal app with an SPFx package, you have the option of deploying the package to the SharePoint tenant app catalog and clicking on the "Sync to Teams" button which then makes the app available in Teams as shown here: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/integrate-with-teams-introduction

But what if you are coming at it from the Teams app point of view? You already have a Teams app with a bot or a messaging extension and want to add the SPFx powered personal app to it. You probably don't want to use the "Sync to Teams" option in this case because then your SPFx web-part will be available as a separate app in the Teams app catalog.

Fortunately, it's very simple to define a staticTab in the Teams manifest which points to the SPFx webpart. This then makes the SPFx webpart available as a teams personal app:

Notice the teams and personal query string parameters as they are very important. You will also have to replace the component id with the id of your web-part.

I have also updated the official MS Docs with this approach:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/creating-team-manifest-manually-for-webpart

Thanks to my colleague Jarbas for working with me in figuring out this one!