Friday, 4 September 2020

Microsoft Teams messaging extensions using SPFx: Getting the message data with Microsoft Graph

With SPFx 1.11, one of the things possible now is that SharePoint Framework web parts can be exposed as Microsoft Teams messaging extensions. So what are messaging extensions exactly? According to the Teams docs:

"Messaging extensions allow users to interact with your web service through buttons and forms in the Microsoft Teams client. They can search, or initiate actions, in an external system from the compose message area, the command box, or directly from a message. You can then send the results of that interaction back to the Microsoft Teams client, typically in the form of a richly formatted card."

https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/what-are-messaging-extensions

As a Microsoft 365 Developer, messaging extensions are a great way to invoke custom code right in the Teams client. This opens up the possibility of users interacting with your application right in the context of their conversations without having to leave Teams.

The SPFx docs give a nice overview of how to setup web parts so that they are exposed as compose  extensions. This enables the custom SPFx webpart to be invoked from the "Compose new message" box in Teams: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-expose-webparts-teams#expose-web-part-as-microsoft-teams-messaging-extension 

In this post, we are going to be talking about SPFx webparts being hosted in task modules which show up in "message actions" i.e. invoking custom code on messages which are already posted in Teams. This could be either in channels or in personal or group chats.

(click to zoom)

Now behind the scenes, when a message action is invoked on a message, we want to get the message context passed to our SharePoint Framework web part. By message context, I mean properties like teams id and channel id in which the action was invoked. If the message action was invoked in a personal chat or a group chat, then we need to know the chat id instead. And finally, we need the data about the message itself e.g. message id, message body, who posted the message etc. so that we can then send the information to our application right from the SPFx webpart.

Now if we were using the Bot Framework to power our message action (and task module), then getting these properties is straightforward as every time the message action is invoked, the Bot Framework sends this information to our messaging endpoint: https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/action-commands/create-task-module?tabs=json#example-fetchtask-request

When using SharePoint Framework however, we have to take a longer route. When the message action would be invoked on a Teams message: Although we get the context information like team id, channel id and chat id, all we will get about the message itself is just the id. No other data about the message like the body, user etc will be available. Getting all these other details would be up to us. Let's see how we do that:

Teams app manifest


First, to get the SPFx powered message action working, we need to configure it in the Teams manifest.

Notice that the fetchTask property is set to false. This makes the task module defined in the manifest to be displayed. (If fetchTask is set to true, the Teams will go to the Bot messaging endpoint to get the task module dynamically)

Also notice that the url for a SharePoint Framework Task module is slightly different compared to a Teams tab

SPFx and Microsoft Graph:

Although we won't have the message data directly provided to us in SPFx, we would have all the context information necessary to fetch the data. As part of the microsoftTeams context object, we will have the teamId, channelId, chatId and the parentMessage. We can then use these details along with the Microsoft Graph to get the message details:

Before we go through the code, make sure that the SPFx solution has the Chat.Read permissions on the Microsoft Graph configured in the package-solution.json file. This will allow us to read the Teams messages on behalf of the currently logged in user


And finally, here is the SPFx code to get the message details on which the message action was invoked:

Hope you found the post useful! Here is the SPFx webpart code on GitHub: https://github.com/vman/spfx-teams-message-action

Note:

This approach currently works with Teams messages in personal chats, group chats and top level messages in Teams channels. There seems to be a gap right now where this approach does not work for replies posted to top level teams messages. I have opened up a GitHub issue about this and will post a follow up soon. https://github.com/OfficeDev/microsoft-teams-library-js/issues/398