Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building Java Messaging Extensions and Connectors for Microsoft Teams Part 2: Building a Link Unfurling Messaging Extension

0.00/5 (No votes)
31 Jan 2022 1  
How to use Java and Spring MVC to implement link unfurling in Microsoft Teams apps
This article demonstrates how to create a Spring Boot web app in Java that replicates the functionality of the “Universal Bots” sample app on GitHub.

In the first article of this three-part series, we discussed how to build a messaging extension for search. In this article, we’ll learn how to make a link unfurling messaging extension.

Exploring Link Unfurling

Link unfurling enables you to invoke a method in your app whenever the user pastes a link with a particular domain to the Microsoft Teams message compose area. This action is helpful in many situations. For instance, you can parse the URL and provide the user with additional buttons so the user can perform a task without visiting the website and reading its contents. This ability accelerates many tasks your users perform within Microsoft Teams while minimizing distractions.

Here’s an example of automatic link unfurling for azure.microsoft.com:

After the user pastes the link, Microsoft Teams automatically displays the most relevant information.

This article will explore how to build the link unfurling using Java and Spring MVC. Adding link unfurling is similar to building messaging extensions: we first register and implement the bot, then prepare the application manifest. We add at least one command for the messaging extension (otherwise, the manifest won’t be installable). Then, we configure the message handlers to provide the list of domains that trigger link unfurling.

As before, we will use a local environment with ngrok. The application’s complete source code is available on GitHub.

Creating a Project

We can use the Yeoman generator to create the project:

yo botbuilder-java

We configure the bot as follows:

  • Name: link-unfurling
  • Package name: db.teams.linkunfurling
  • Template: Empty Bot

This command creates the new project with an EmptyBot class (src/main/java/EmptyBot.java):

Java
public class EmptyBot extends ActivityHandler { }

Let’s rename the file and the class name to LinkUnfurling, then modify the base class to TeamsActivityHandler:

Java
public class LinkUnfurling extends TeamsActivityHandler { }

To make this work, we also need to modify the getBot method in Application.java:

Java
@Bean
public Bot getBot() {
    return new LinkUnfurling();
}

Implementing the Bot

We now need to register the bot. It’s essential to implement the bot before creating the manifest. Otherwise, the link unfurling might cache the results. If you change anything in the method handling the link unfurling invoke, you often need to re-install the Teams application.

We proceed the same as in the previous article to register the bot. So, let’s register the new bot link-unfurling-bot and configure its endpoint to either a local ngrok forwarding URL or the actual URL if you decide to deploy the bot to the Internet — for example, using Azure Spring Cloud.

Then, generate the bot password and paste it and the bot ID to application.properties:

Using the Search Command

We need to have at least one command for the messaging extension to make the manifest installable. So, we use the previously developed echo-like search command handler. The only difference here is that we have the hard-coded hero card text ("Link unfurling"):

Java
@Override
protected CompletableFuture<MessagingExtensionResponse> onTeamsMessagingExtensionQuery(
        TurnContext turnContext,
        MessagingExtensionQuery query
) {
    // Get query text
    String queryText = GetQueryText(query);
   
    // Create a hero card
    HeroCard card = new HeroCard();
    card.setTitle("Echo");
    card.setSubtitle(queryText);
    card.setText("Link unfurling");

    // Create attachment
    MessagingExtensionAttachment attachment = new MessagingExtensionAttachment();
    attachment.setContent(card);
    attachment.setContentType(HeroCard.CONTENTTYPE);
    attachment.setPreview(card.toAttachment());

    // Prepare result
    MessagingExtensionResult result = new MessagingExtensionResult();
    result.setAttachmentLayout("list");
    result.setType("result");
    result.setAttachment(attachment);

    // Return the response
    return CompletableFuture.completedFuture(new MessagingExtensionResponse(result));      
}

GetQueryText is the helper method. It extracts the search query text from the parameters it received from the messaging extension. It works as the previous article explains.

The above code generates results like the screenshot below:

Implementing Link Unfurling

When the user pastes a link containing a domain registered under the message handlers, the Bot Framework invokes the onTeamsAppBasedLinkQuery method. To im­­­plement link unfurling, all you need to do is to override the onTeamsAppBasedLinkQuery method in your Bot’s class. Here’s the example for the LinkUnfurling class:

Java
public class LinkUnfurling extends TeamsActivityHandler {

    @Override
    protected CompletableFuture<MessagingExtensionResponse> onTeamsAppBasedLinkQuery(
        TurnContext turnContext,
        AppBasedLinkQuery query
    ) { }

}

As shown above, the Framework provides you with two objects: TurnContext and AppBasedLinkQuery. TurnContext supplies the information needed to process the incoming request. Specifically, you can use it to send responses back to the user. The AppBasedLinkQuery class represents the request body for the app-based link query. It contains two properties:

  • Url, which contains the URL pasted by the user
  • State, which contains the magic code for OAuth flow

Here, we’ll use only the first property. More specifically, we first create the ThumbnailCard, and set its title to CodeProject and the text to the URL that the user pasted. Additionally, we set the image to the CodeProject logo:

Java
ThumbnailCard card = new ThumbnailCard();
card.setTitle("CodeProject");
card.setText(query.getUrl());

final String logoLink = 
 "https://codeproject.freetls.fastly.net/App_Themes/CodeProject/Img/logo250x135.gif";

CardImage cardImage = new CardImage(logoLink);
card.setImages(Collections.singletonList(cardImage));

Then, we create an attachment (in the same way as for the messaging extension):

Java
// Create attachments
MessagingExtensionAttachment attachments = new MessagingExtensionAttachment();
attachments.setContentType(HeroCard.CONTENTTYPE);
attachments.setContent(card);

We attach the attachment to the MessagingExtensionResult class instance as follows:

Java
// Result
MessagingExtensionResult result = new MessagingExtensionResult();
result.setAttachmentLayout("list");
result.setType("result");
result.setAttachments(Collections.singletonList(attachments));

Finally, we generate and return the response:

Java
// MessagingExtensionResponse
return CompletableFuture.completedFuture(new MessagingExtensionResponse(result));

Here’s the complete method:

Java
@Override
protected CompletableFuture<MessagingExtensionResponse> onTeamsAppBasedLinkQuery(
    TurnContext turnContext,
    AppBasedLinkQuery query
) {
    // Create ThumbnailCard
    ThumbnailCard card = new ThumbnailCard();
    card.setTitle("CodeProject");
    card.setText(query.getUrl());

    final String logoLink = 
    "https://codeproject.freetls.fastly.net/App_Themes/CodeProject/Img/logo250x135.gif";
    CardImage cardImage = new CardImage(logoLink);
    card.setImages(Collections.singletonList(cardImage));

    // Create attachments
    MessagingExtensionAttachment attachments = new MessagingExtensionAttachment();
    attachments.setContentType(HeroCard.CONTENTTYPE);
    attachments.setContent(card);

    // Result
    MessagingExtensionResult result = new MessagingExtensionResult();
    result.setAttachmentLayout("list");
    result.setType("result");
    result.setAttachments(Collections.singletonList(attachments));

    // MessagingExtensionResponse
    return CompletableFuture.completedFuture(new MessagingExtensionResponse(result));
}

Creating the Manifest

To test our link unfurling app, we need to create the manifest and install the Microsoft Teams app. We proceed precisely the same as the previous article to make the manifest. We just need to enable link unfurling by adding message handlers. Message Handlers is the last configuration option you see in the Messaging Extensions screen. You can configure the domains as explained in Microsoft’s documents. Here, we add only one domain: www.codeproject.com.

For your convenience, I uploaded the final manifest and the companion code on GitHub. You’ll still need to update line 28 with your bot identifier. Then, zip the manifest.json along with the attached icons. Afterward, use Import an existing app in App Studio or the Developer Portal.

Putting Everything Together

Now build (mvn clean install) and start your web service (java -jar target/link-unfurling-1.0.0-SNAPSHOT.jar). Then, open Microsoft Teams, go to the chat window and paste any link from codeproject.com.

For example:

https://www.codeproject.com/Articles/5299822/Java-on-Azure-Adding-Containers

You’ll see the following result:

Next Steps

Here, we learned how to use Java and Spring MVC to implement link unfurling in Microsoft Teams apps. We started by creating the Empty Bot project template, then added a handler for link unfurling. Finally, we prepared the app manifest.

Creating the link unfurling is a two-step process. You first build the messaging extension with at least one command, then specify the list of domains. When a user pastes a link containing one of those domains, the Bot Framework invokes the onTeamsAppBasedLinkQuery method of your bot’s class. In this method, you can prepare the response in the form of a card.

Continue to the third and final part of this three-part series, where we’ll explore building webhooks and notification connectors.

To learn how you can build, migrate and scale Java applications on Azure using Azure services, check out Get started with Java on Azure.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here