Skip to content

How to extend Link Unfurling template

yukun-dong edited this page Jun 2, 2023 · 16 revisions

How to use Zero Install Link Unfurling in Teams

Zero Install Link Unfurling requires link unfurling app to be published. You need an admin account to publish an app into your org.

Login your admin account in Teams. Go to Manage your apps -> Upload an app. Click Upload an app to your org's app catalog to upload your app's zip file.

upload

Switch to another user account. Without installing this app, paste the link "https://www.botframework.com" into chat box, and you should see the adaptive card like below.

zeroInstall

How to add link unfurling cache in Teams

This template removes cache by default to provide convenience for debug. To add cache, REMOVE following JSON part from adaptive card in linkUnfurlingApp.ts (linkUnfurlingApp.js):

suggestedActions: {
          actions: [
            {
              title: "default",
              type: "setCachePolicy",
              value: '{"type":"no-cache"}',
            },
          ],
        }

After removing this, the link unfurling result will be cached in Teams for 30 minutes. Please refer to link unfurling document for more details.

How to customize Zero Install Link Unfurling's adaptive cards

The supported types for Zero Install Link Unfurling are "result" and "auth" and this template uses "result" as default. By changing it to "auth", the adaptive card will be:

zeroInstallAuth

For card with type "auth", the Teams client strips away any action buttons from the card, and adds a sign in action button. Please refer to zero install link unfurling document for more details.

How to add stage view

Stage View is a full screen UI component that you can invoke to surface your web content. You can turn URLs into a tab using an Adaptive Card and Chat Services. Follow the instructions below to add stage view in your link unfurling app.

Step 1: Update staticTabs in manifest.json

In appPackage/manifest.json, update staticTabs section.

    "staticTabs": [
        {
            "entityId": "stageViewTask",
            "name": "Stage View",
            "contentUrl": "https://${{BOT_DOMAIN}}/tab",
            "websiteUrl": "https://${{BOT_DOMAIN}}/tab",
            "searchUrl": "https://${{BOT_DOMAIN}}/tab",
            "scopes": [
                "personal"
            ],
            "context": [
                "personalTab",
                "channelTab"
            ]
        }
    ],

Step 2: Update source code

In src/index.ts (src/index.js), add following code.

server.get("/tab", async (req, res) => {
  const body = `<!DOCTYPE html>
  <html lang="en">
  
  <div class="text-center">
    <h1 class="display-4">Tab in stage View</h1>
  </div>
  
  </html>`;
  res.writeHead(200, {
    'Content-Length': Buffer.byteLength(body),
    'Content-Type': 'text/html'
  });
  res.write(body);
  res.end();
});

Step 3: Set BOT_DOMAIN and TEAMS_APP_ID in environment variables

For local debug:

Update action file/createOrUpdateEnvironmentFile in teamsapp.local.yml, add TEAMS_APP_ID and BOT_DOMAIN to env.

  - uses: file/createOrUpdateEnvironmentFile # Generate runtime environment variables
    with:
      target: ./.localConfigs
      envs:
        BOT_ID: ${{BOT_ID}}
        BOT_PASSWORD: ${{SECRET_BOT_PASSWORD}}
        TEAMS_APP_ID: ${{TEAMS_APP_ID}}
        BOT_DOMAIN: ${{BOT_DOMAIN}}

For remote:

Update infra/azure.parameters.json. Add following to parameters:

    "teamsAppId":{
      "value": "${{TEAMS_APP_ID}}"
    }

Add following to infra/azure.bicep:

param teamsAppId string 

resource webAppSettings 'Microsoft.Web/sites/config@2022-09-01' = {
  parent: webApp
  name: 'appsettings'
  properties: {
    BOT_DOMAIN: webApp.properties.defaultHostName
    BOT_ID: botAadAppClientId
    BOT_PASSWORD: botAadAppClientSecret
    RUNNING_ON_AZURE: '1'
    TEAMS_APP_ID: teamsAppId
    WEBSITE_NODE_DEFAULT_VERSION: '~18'
    WEBSITE_RUN_FROM_PACKAGE: '1'
  }
}

Step 4: Update adaptive card

In src/adaptiveCards/helloWorldCard.json, update actions to be following.

"actions": [
        {
            "type": "Action.OpenUrl",
            "title": "View Via Deep Link",
            "url": "https://teams.microsoft.com/l/stage/${appId}/0?context=%7B%22contentUrl%22%3A%22https%3A%2F%2F${url}%2Ftab%22%2C%22websiteUrl%22%3A%22https%3A%2F%2F${url}%2Ftab%22%2C%22name%22%3A%22DemoStageView%22%7D"
        }
      ],

Run npm install @microsoft/adaptivecards-tools. This package helps render placeholders such as ${url} in adaptive card to be real values.

In src/linkUnfurlingApp.ts (src/linkUnfurlingApp.js), update variable attachment to be following.

    const data = { url: process.env.BOT_DOMAIN, appId: process.env.TEAMS_APP_ID };

    const renderedCard = AdaptiveCards.declare(card).render(data);

    const attachment = { ...CardFactory.adaptiveCard(renderedCard), preview: previewCard };

In Teams, the adaptive card will be like:

stageView

Opening stage view from Adaptive card via deep link:

viaDeepLink

In Outlook, the adaptive card will be like:

stageView

Opening stage view from Adaptive card via deep link:

viaDeepLink

Please refer to Stage view document for more details.

How to add task module (Teams)

Once your link is unfurled into an Adaptive Card and sent in conversation, you can use Task modules to create modal pop-up experiences in your Teams application. Follow the instructions below to add task module in your link unfurling app.

Step 1: Update adaptive card

In src/adaptiveCards/helloWorldCard.json, update actions to be following.

    "actions": [
        {
            "type": "Action.Submit",
            "title": "Task module",
            "data": {
                "msteams": {
                    "type": "task/fetch",
                    "data": "task module"
                }
            }
        }
      ],

Step 2: Add handleTeamsTaskModuleFetch function in handler

For typescript template, in src/linkUnfurlingApp.ts, add following method to LinkUnfurlingApp class.

  public async handleTeamsTaskModuleFetch(context: TurnContext, taskModuleRequest: TaskModuleRequest): Promise<TaskModuleResponse> {
    return {
      task: {
        type: "continue",
        value: {
          title: "Task Module Fetch",
          height: 200,
          width: 400,
          card: CardFactory.adaptiveCard({
            version: '1.0.0',
            type: 'AdaptiveCard',
            body: [
              {
                type: 'TextBlock',
                text: 'Enter Text Here'
              },
              {
                type: 'Input.Text',
                id: 'usertext',
                placeholder: 'add some text and submit',
                IsMultiline: true
              }
            ],
            actions: [
              {
                type: 'Action.Submit',
                title: 'Submit'
              }
            ]
          })
        },
      },
    };
  }

For javascript template, in src/linkUnfurlingApp.js, add following method to LinkUnfurlingApp class.

  handleTeamsTaskModuleFetch(context, taskModuleRequest) {
    return {
      task: {
        type: "continue",
        value: {
          title: "Task Module Fetch",
          height: 200,
          width: 400,
          card: CardFactory.adaptiveCard({
            version: '1.0.0',
            type: 'AdaptiveCard',
            body: [
              {
                type: 'TextBlock',
                text: 'Enter Text Here'
              },
              {
                type: 'Input.Text',
                id: 'usertext',
                placeholder: 'add some text and submit',
                IsMultiline: true
              }
            ],
            actions: [
              {
                type: 'Action.Submit',
                title: 'Submit'
              }
            ]
          })
        },
      },
    };
  }

Step 3: Add handleTeamsTaskModuleSubmit function in handler

For typescript template, in src/linkUnfurlingApp.ts, add following method to LinkUnfurlingApp class.

  public async handleTeamsTaskModuleSubmit(context: TurnContext, taskModuleRequest: TaskModuleRequest): Promise<TaskModuleResponse> {
    return {
      task: {
        type: 'message',
        value: 'Thanks!'
      }
    };
  }

For javascript template, in src/linkUnfurlingApp.js, add following method to LinkUnfurlingApp class.

  handleTeamsTaskModuleSubmit(context, taskModuleRequest) {
    return {
      task: {
        type: 'message',
        value: 'Thanks!'
      }
    };
  }

In Teams, the adaptive card will be like:

taskModule

Click "Task module" button:

taskModuleFetch

Click "Submit" button:

taskModuleSubmit

Please refer to Task module document for more details.

How to add adaptive card action (Teams)

Adaptive Card actions allow users to interact with your card by clicking a button or selecting a choice. Follow the instructions below to add adaptive card action in your link unfurling app.

Step 1: Update bots section in manifest

The card action requires bot capability. In appPackage/manifest.json, update bots section to be following.

    "bots": [
        {
            "botId": "${{BOT_ID}}",
            "scopes": [
                "team",
                "personal",
                "groupchat"
            ],
            "supportsFiles": false,
            "isNotificationOnly": false
        }
    ]

Step 2: Update adaptive card

In src/adaptiveCards/helloWorldCard.json, update actions to be following.

    "actions": [
        {
            "type": "Action.Execute",
            "title": "card action",
            "verb": "cardAction",
            "id": "cardAction"
        }
    ],

Step 3: Add onAdaptiveCardInvoke function in handler

For typescript template, in src/linkUnfurlingApp.ts, add following method to LinkUnfurlingApp class.

  public async onAdaptiveCardInvoke(context: TurnContext, invokeValue: AdaptiveCardInvokeValue): Promise<AdaptiveCardInvokeResponse> {
    const card = {
      "type": "AdaptiveCard",
      "body": [
        {
          "type": "TextBlock",
          "text": "Your response was sent to the app",
          "size": "Medium",
          "weight": "Bolder",
          "wrap": true
        },
      ],
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "version": "1.4"
    };
    const res = { statusCode: 200, type: "application/vnd.microsoft.card.adaptive", value: card };
    return res;
  }

For javascript template, in src/linkUnfurlingApp.js, add following method to LinkUnfurlingApp class.

  onAdaptiveCardInvoke(context, invokeValue) {
    const card = {
      "type": "AdaptiveCard",
      "body": [
        {
          "type": "TextBlock",
          "text": "Your response was sent to the app",
          "size": "Medium",
          "weight": "Bolder",
          "wrap": true
        },
      ],
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "version": "1.4"
    };
    const res = { statusCode: 200, type: "application/vnd.microsoft.card.adaptive", value: card };
    return res;
  }

In Teams, the adaptive card will be like:

cardAction

Click card action button, the adaptive card will be updated to be following:

cardActionClick

Please refer to Universal actions document for more details.

How to Extend this template with Notification, Command and Workflow bot.

The Notification, Command and Workflow Bot are scenario templates provided by Teams Toolkit. These templates have similar structure. This guide takes Notification Bot as an example.

Step 1: Create a Notification Bot template using Teams Toolkit.

Select the Teams Toolkit icon on the left in the VS Code toolbar. Choose "Create a New App"->"Bot"->"Chat Notification Message". Wait for the download complete.

Step 2: Update source code.

Copy all methods from src/linkUnfurlingApp.ts class to Notification Bot's empty TeamsActivityHandler in src/teamsBot.ts.

Step 3: Update manifest.

Copy composeExtension section in your appPackage/manifest.json to Notification bot's appPackage/manifest.json.

Now your Notification bot project has both notification and link unfurling function.

notification link unfurling

Clone this wiki locally