March 26th, 2019

Add rich previews to messages using Link unfurling

One of the most common ways to share content in Microsoft Teams is through links, be it for an update for that task you’re working on or for sharing the latest Game of Thrones trailer. For any publicly accessible link Teams already shows a preview of the link including information like an image, title and a description.

But starting today, we’re allowing apps to customize these previews for domains that it’s interested in. Apps can register a message handler for domains that they want to serve, and in doing so will get a queryLink event every time a link of that domain is entered in the compose box.

By default the content is shown in the standard URL preview format supported in Teams, but users have the option to expand to the full card if they wish to do so.

How to add it to your app?

To enable your app to interact with links, you need to add a messageHandlers array to your app manifest, as in the example below: Note that this article assumes that you already have an app with a messaging extension capability, if not please do have a look at our documentation on messaging extensions.

"composeExtensions": [
{
    "botId": "abc123456-ab12-ab12-ab12-abcdef123456",
    "messageHandlers": [
        {
            "type": "link",
            "value": {
                "domains": [
                    "*.trackeddomain.com"
                ]
            }
        }
    ]
}
]

Once you’ve added the domain to listen on to the app manifest, you’ll need to change your bot code to respond to the below invoke request.

{
  "type": "invoke",
  "name": "composeExtension/queryLink",
  "value": {
    "url": "https://theurlsubmittedbyyouruser.trackeddomain.com/id/1234"
  }
}

In your app you can use the onAppBasedLinkQuery handler to listen to this event and build a response. Here’s the sample code to building the response to the event in Javascript:

require('dotenv').config();
import * as restify from 'restify';
import * as builder from 'botbuilder';
import * as teamBuilder from 'botbuilder-teams';

class App {
    run() {
        const server = restify.createServer();
        let teamChatConnector = new teamBuilder.TeamsChatConnector({
            appId: process.env.MICROSOFT_APP_ID,
            appPassword: process.env.MICROSOFT_APP_PASSWORD
        });

        // Command ID must match what's defined in manifest
        teamChatConnector.onAppBasedLinkQuery(
            (event: builder.IEvent,
            query: teamBuilder.ComposeExtensionQuery,
            callback: (err: Error, result: teamBuilder.IComposeExtensionResponse, statusCode: number) => void) => {

                let response = teamBuilder.ComposeExtensionResponse.result('list').attachments([
                    new builder.ThumbnailCard()
                        .title('Test thumbnail card')
                        .text('[1234]: Create a cool app ')
                        .images([new builder.CardImage().url('https://bot-framework.azureedge.net/bot-icons-v1/bot-framework-default-9.png')])
                        .toAttachment()
                ]).toResponse();

                callback(null, response, 200);
            });
        server.post('/api/composeExtension', teamChatConnector.listen());
        server.listen(process.env.PORT, () => console.log(`listening to port:` + process.env.PORT));
    }
}
const app = new App();
app.run();

This will result in the following response being sent to the client, and will be used to build the Link preview.

{
  "composeExtension": {
    "type": "result",
    "attachmentLayout": "list",
    "attachments": [
      {
        "contentType": "application/vnd.microsoft.teams.card.o365connector",
        "content": {
          "sections": [
            {
              "activityTitle": "[1234]: Create a cool app",
              "activityImage": "https://placekitten.com/200/200"
            },
            {
              "title": "Details",
              "facts": [
                {
                  "name": "Assigned to:",
                  "value": "[Larry Brown](mailto:larryb@example.com)"
                },
                {
                  "name": "State:",
                  "value": "Active"
                }
              ]
            }
          ]
        },
        "preview": {
          "contentType": "application/vnd.microsoft.card.thumbnail",
          "content": {
            "title": "1234: Create a cool app",
            "images": [
              {
                "url": "https://placekitten.com/200/200"
              }
            ]
          }
        }
      }
  }
}

Note that if your app responds with more than one preview for a link only the first one will be considered.

What if the user needs to sign in or setup the app?

Just like in the standard messaging extension response, responses for auth and setup are both supported. On receiving these, the Teams client will show an inline message to the user asking them to complete the sign in or setup.

Try it out yourself!

App based link previews are in Developer preview today do try it out and reach out to us for any questions or issues you run into.