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;
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.
Conclusion, I removed the empty containers and the flow started to work in the cloud.