You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Teams安卓端Adaptive Card触发Action.Execute时出现短暂错误提示

版本信息

  • 包:botbuilder@4.11.0
  • Node.js:v16.19.0
  • Teams安卓客户端:1416/1.0.0/2023012702/0115

我开发了一款通知类聊天机器人,会向用户发送Adaptive Card通知。用户可在卡片的Input.Text字段输入回复内容,点击触发Action.Execute的发送按钮后,Node.js机器人会处理该活动:向对应用户发送通知,并更新卡片内容。

该功能在Web端和桌面端运行完全正常,但在Teams安卓客户端中,卡片最终完成更新前,会短暂显示"Something Went Wrong"错误提示。

甚至无论我对卡片执行什么操作,即使仅返回InvokeResponse不做任何其他处理,该错误仍会出现。

发送的卡片代码

{
      "type": "AdaptiveCard",
      "appId": process.env.MicrosoftBotAppId,
      "body": [
        {
          "type": "TextBlock",
          "text": message,
          "wrap": true
        },
        {
          "id": "history_title",
          "type": "TextBlock",
          "text": cardString.pastEvents,
          "wrap": true
        },
        {
          "type": "Container",
          "spacing": "None",
          "items": [
            {
              "id": "last_trail_name",
              "type": "TextBlock",
              "text": lastTrail ? cardString.action(lastTrail.userName, lastTrailAction) : "",
              "spacing": "None",
              // "size": "Small",
              "wrap": true
            },
            {
              "id": "last_trail_comment",
              "type": "TextBlock",
              "text": lastTrail ? `${lastTrail.comment.replace(/<\/?[^>]+(>|$)/g, "")}` : "",
              "spacing": "None",
              // "size": "Small",
              "wrap": true
            },
            {
              "id": "last_trail_date",
              "type": "TextBlock",
              "text": lastTrail ? `${lastTrailDateString}` : "",
              "spacing": "None",
              "isSubtle": true,
              "size": "Small",
              "wrap": true
            }
          ]
        },
        {
          "type": "Container",
          "items": [
            {
              "id": "second_last_trail_name",
              "type": "TextBlock",
              "text": secondLastTrail ? cardString.action(secondLastTrail.userName, secondLastTrailAction) : "",
              "spacing": "None",
              // "size": "Small",
              "wrap": true
            },
            {
              "id": "second_last_trail_comment",
              "type": "TextBlock",
              "text": secondLastTrail ? `${secondLastTrail.comment.replace(/<\/?[^>]+(>|$)/g, "")}` : "",
              "spacing": "None",
              // "size": "Small",
              "wrap": true
            },
            {
              "id": "second_last_trail_date",
              "type": "TextBlock",
              "text": secondLastTrail ? `${secondLastTrailDateString}` : "",
              "spacing": "None",
              "isSubtle": true,
              "size": "Small",
              "wrap": true
            }
          ]
        },
        {
          "id": "comment",
          "isRequired": true,
          "type": "Input.Text",
          "placeholder": cardString.commentPlaceholder,
          "isMultiline": true
        },
        {
          "id": "success-msg",
          "type": "TextBlock",
          "text": success ? cardString.commentSentAlert : "",
          "spacing": "None",
          "isSubtle": true,
          "size": "Small",
          "wrap": true,
          "color": "good"
        }
      ],
      "actions": [
        {
          // "tooltip": isPremium ? cardString.send : cardString.premiumTooltip,
          // "isEnabled": isPremium, 
          "type": "Action.Execute",
          "verb": "comment",
          "title": cardString.send,
          "data": data,
        },
        {
          "type": "Action.OpenUrl",
          "title": cardString.goToTicketButton,
          "url": deeplink
        }
      ],
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "version": "1.4"
    }

处理动作的机器人代码

class Bot extends TeamsActivityHandler {
    constructor() {
      super();

      this.onMembersAdded(async (turnContext: TurnContext, next: () => Promise<any>) => {
        const gettingStartedUrl: string = 'https://www.teamswork.app/gettingstarted';
        const membersAdded = turnContext.activity.membersAdded;
        for (let cnt = 0; cnt < membersAdded.length; cnt++) {
          if (membersAdded[cnt].id !== turnContext.activity.recipient.id) {
            const welcomeMessage = "Welcome message"

            // add conv references
            if (await this.storeConversationReference(turnContext))
              await turnContext.sendActivity(welcomeMessage); // Only send notification if conversation reference store (one time)
          }
        }

        // By calling next() you ensure that the next BotHandler is run.
        await next();
      });
    }
    // Listener for activity e.g. comment activity on notification card
    async onInvokeActivity(turnContext: TurnContext): Promise<InvokeResponse<any>> {
      // Listen for activity invocation and check if the comment action on adaptiveCard has been called
      const activity: Activity = turnContext.activity;
      // context.log('activity: ', activity);
      // turnContext.sendActivity("Hello World!");
      let card: object;

      if (activity.name === "adaptiveCard/action") {
        // context.log("action activity");
        const action = activity.value.action;
        // context.log("action : ", action);
        if (action.verb === "comment") {
          const userName: string = activity.from.name;
          const userId: string = activity.from.aadObjectId;
          const data: IActionData = action.data;
          // context.log('data: ', action.data);
          const date: Date = new Date();
          const tenantId: string = activity.conversation.tenantId;
          const comment: string = data.comment;
          const lang: string = data.lang || "en";

          // assignee Ticket == user commennt
          if ((data.ticket.assigneeId == userId) && (data.ticket.firstTimeResponse == "")) {
            this.updateTicket(data.ticket)
          }
          // Check for ticketId
          if (!!!data.ticket) {
            context.log("No ticketId found, aborting");
            // throw ("No ticketId found, aborting")
          }

          // Construct new audit trail / comment
          let newComment: IAuditTrail = {
            userName: userName,
            userId: userId,
            ticketId: data.ticket.id,
            action: "commented",
            comment: comment,
            date: date,
          }

          // initialize parameters for card to be sent
          const cardContent: ICardContent = {
            userName: userName,
            type: "comment",
            action: "commented",
            comment: comment,
            isRequestor: false,
          }

          // Prepare response message
          const encodedDeeplink: string = constructDeeplink(data.ticket, data.ticketingAppUrl, data.channelId, data.entityId);
          const message: string = constructMessage(data.ticket, data.cardContent, encodedDeeplink, lang);
          const isPremium: boolean = await getPremium(tenantId);
          // const card = invokeSuccessResponse(encodedDeeplink, message, data);       

          // Save audit trail
          const insertCommentEndpoint: string = process.env["InsertCommentEndpoint"];
          try {
            await cosmosService.insertItem(insertCommentEndpoint + encodeQueryParams({ instanceId: data.ticket.instanceId }), { comment: newComment });
          } catch (error) {
            context.log(error);
          }

          // Send request to SendCardNotification API
          try {
            // Notification recipient id array
            let recipientIds: string[] = [];
            if (data.ticket.requestorId)
              recipientIds.push(data.ticket.requestorId)
            if (data.ticket.assigneeId)
              recipientIds.push(data.ticket.assigneeId)

            // If ticket have custom fields get people from them
            if (data.ticket.customFields) {
              // Get instance so we can get customFieldSetting
              const instanceContainer: Container = ticketDatabase.container(process.env["InstanceContainer"]);
              const { resource: instance } = await instanceContainer.item(data.ticket.instanceId, tenantId).read<IInstance>();

              // Create array for user who will receive the notification
              let activePersonaCustomFields: ICustomField[] = [];
              if (instance.customFieldsLeft)
                activePersonaCustomFields.push(...instance.customFieldsLeft);
              if (instance.customFieldsRight)
                activePersonaCustomFields.push(...instance.customFieldsRight);
              activePersonaCustomFields = activePersonaCustomFields.filter((value) => value.type.key === "6_peoplepicker");
              activePersonaCustomFields = activePersonaCustomFields.filter((value) => value.isReceiveNotification);

              if (activePersonaCustomFields.length > 0) {
                for (const fields of activePersonaCustomFields) {
                  if (data.ticket.customFields[fields.id]?.length > 0) {
                    const personas: IPersonaProps[] = data.ticket.customFields[fields.id];

                    for (const element of personas) {
                      recipientIds.push(element.id)
                    }

                  }
                }
              }
            }

            // Remove message sender from recipient list
            recipientIds = recipientIds.filter((id) => id !== userId);

            // Get unique notification recipient array
            const uniqueRecipientIds: string[] = [... new Set(recipientIds)];

            context.log("Sending notifications");
            for (const id of uniqueRecipientIds) {
             await this.sendNotification(cardContent, id, tenantId, data);
            }

            card = NotificationCard(encodedDeeplink, message, data, true, isPremium, lang);
            const cardAttachment: Attachment = CardFactory.adaptiveCard(card);
            const activityPayload = MessageFactory.attachment(cardAttachment);
            activityPayload.id = turnContext.activity.replyToId;

            context.log("Updating cards");
            await turnContext.updateActivity(activityPayload);
            
          } catch (error) {
            context.log(error)
          }
        }
      }
      const cardRes = {
        statusCode: StatusCodes.OK,
        type: 'application/vnd.microsoft.card.adaptive',
        value: card
      };
      const res = {
        status: StatusCodes.OK,
        body: cardRes
      };
      return res;
    }
  }

预期行为

卡片更新时不显示任何错误提示。

截图

WhatsApp Image 2023-02-10 at 17 00 37

内容的提问来源于stack exchange,提问作者ingmar ramzan shidqi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 08:45:46