botframeworkazure-bot-serviceadaptive-cardsteams-toolkit

Adaptive Card works locally but fails when deployed in Azure


I've built a Teams app with a notification bot that uses adaptive cards. The interaction with the adaptive card works perfectly in my local environment, but fails when deployed in Azure App Service (Windows OS). Locally, the interaction triggers onInvokeActivity, but in the cloud, it goes through onMessage.

Here is the relevant code:

const { TeamsActivityHandler, StatusCodes, MessageFactory, CardFactory } = require("botbuilder");
const { updateInstanceState } = require("./merlinOperations");


// An empty teams activity handler.
// You can add your customization code here to extend your bot logic if needed.
class TeamsBot extends TeamsActivityHandler {
  constructor() {
    super();

    this.onMessage(async (context, next) => {

      console.info("llegamos hasta el onMessage ", context.activity);
      if (context.activity.name === "adaptiveCard/action") {
        console.info("Activity information ", context.activity);

        let invokeResponse = await this.onInvokeActivity(context);
        console.info("invokeResponse information ", invokeResponse);

        let message = MessageFactory.attachment(CardFactory.adaptiveCard(invokeResponse.body.value));
        message.id = context.activity.replyToId;
        console.info("new message information ", message);

        await context.updateActivity(message);
      }
      else {
        await context.sendActivity("Este bot es solo para generar notificaciones desde merlin");
      }

      //This next calls the next event(Middleware) in case we have many of them.
      await next();
    });
  }

  //This is the action triggered when interaction with an adaptive card.
  async onAdaptiveCardInvoke(context) {
    try {
      let invokeResponse = { statusCode: StatusCodes.OK };
      //if trigger === manual means that the action is a result of an user interaction.
      if (context.activity.name === "adaptiveCard/action" && context.activity.value.trigger === "manual") {
        //Here we go to Merlin an update the instance.
        const successInteractionTemplate = await updateInstanceState(context.activity);

        invokeResponse = InvokeResponseFactory.adaptiveCard(successInteractionTemplate);
        await context.sendActivity({
          type: ActivityTypes.InvokeResponse,
          value: invokeResponse,
        });
      }
      //if trigger === automatic means that the action is a result of an automatic refresh action
      else if (context.activity.name === "adaptiveCard/action" && context.activity.value.trigger === "automatic") {
        //If the validation result is undefined it means that the card has not been used yet.
        const successInteractionTemplate = await validateAdaptiveCardInteraction(context.activity);

        if (typeof (successInteractionTemplate) !== "undefined") {
          //If the card had been used we replace it with the updated information.
          invokeResponse = InvokeResponseFactory.adaptiveCard(successInteractionTemplate);
          await context.sendActivity({
            type: ActivityTypes.InvokeResponse,
            value: invokeResponse,
          });
        }
      }

      return invokeResponse;
    } catch (error) {
      console.error("###ERROR ocurrió en el invoke ", error);

      const errorResponse = InvokeResponseFactory.errorResponse(StatusCodes.BAD_REQUEST, `An error occurred while processing the action. ${error.message || error}`);
      console.error("Error response", JSON.stringify(errorResponse, null, 2));

      await context.sendActivity({
        type: ActivityTypes.InvokeResponse,
        value: errorResponse,
      });

      return errorResponse;
    }
  };
}

module.exports.TeamsBot = TeamsBot;

As in the cloud context.activity.name comes empty, I have commented the if in this statement in order to see if it works, I can confirm the adaptive card to replace comes in the invokeResponse body but the card just shows a message sayin:

Something went wrong. Please try again.

Any insights on why the interaction works locally but fails in Azure would be appreciated.

I have also tried this which works locally(Focus in the onAdaptiveCardInvoke event)

const { TeamsActivityHandler, StatusCodes, MessageFactory, CardFactory, ActivityTypes } = require("botbuilder");
const { updateInstanceState } = require("./merlinOperations");
const { successInteractionStatusChange } = require('./adaptiveCards/templates');
const { InvokeResponseFactory } = require("@microsoft/teamsfx");
const { HttpStatusCode } = require("axios");
const { AdaptiveCard } = require("@microsoft/adaptivecards-tools");


// An empty teams activity handler.
// You can add your customization code here to extend your bot logic if needed.
class TeamsBot extends TeamsActivityHandler {
  constructor() {
    super();

    this.onMessage(async (context, next) => {

      if (context.activity.name === "adaptiveCard/action") {

        let invokeResponse = await this.onInvokeActivity(context);

        await context.sendActivity({
          type: ActivityTypes.InvokeResponse,
          value: invokeResponse,
        });
      }
      else {
        await context.sendActivity("Este bot es solo para generar notificaciones desde merlin");
      }

      //This next calls the next event(Middleware) in case we have many of them.
      await next();
    });
  }

  async onAdaptiveCardInvoke(context) {
    try {
      //Here we confirm the interaction came from an adaptive card
      if (context.activity.name === "adaptiveCard/action") {
       
        let invokeResponse = InvokeResponseFactory.adaptiveCard(successInteractionStatusChange);
        
        invokeResponse.statusCode = StatusCodes.OK;
        
        await context.sendActivity({
          type: ActivityTypes.InvokeResponse,
          value: invokeResponse,
        });
        return invokeResponse;


      }
    } catch (error) {
      //If there was any error we go ahead an throw a toaster with saying an erro has occured
      console.info("###ERROR ocurrió en el invoke ", error)
      const cardRes = {
        statusCode: StatusCodes.OK,
        type: 'application/vnd.microsoft.error',
        value: { message: `ha ocurrido un error ${error.message}` }
      };

      const res = {
        status: StatusCodes.OK,
        body: cardRes
      };
      return res;
    }
  };
}

module.exports.TeamsBot = TeamsBot;


Solution

  • I found that the reason why the card was not rendered in the cloud was because the new card had 3 empty containers that somehow flew the answer when trying to get rendere.

    Before: enter image description here After: enter image description here

    Conclusion, I removed the empty containers and the flow started to work in the cloud.