2

I have a bot developed using the Bot Framework v4 using NodeJS and deployed on multiple channels in Teams. Is there a way we can update a message sent by the bot? I tried implementing the updateActivity() function in the BotFrameworkAdapter. However, it does not update the activity.

enter image description here

I have this card sent from the bot to a Teams channel. When someone clicks on the button, is there a way I can update the card or the message (disabling the button)?

Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
Pratik Mathur
  • 117
  • 3
  • 11

2 Answers2

5

The key to this is making sure that when you use updateActivity(), you use the right activity ID that is created by the Teams Channel. You also need to make sure that the updated activity gets all of the Teams data set to it.

In onTurn, capture outgoing activities so that you can easily save all of the necessary Teams Channel data:

public onTurn = async (turnContext: TurnContext) => {

    turnContext.onSendActivities(async (ctx, activities, nextSend) => {
        activities.forEach(async (activity) => {
            if (activity.channelData.saveMe) {
                this.savedActivity = activity;
            }
        });
        return await nextSend();
    });
  • Note: There might be another way to do this. I just found this to be the easiest, since you need to save all of the channelData, conversation info, and activity.id, at a minimum
  • How you store that activity to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.
  • Activities keep all channelData. By specifying a saveMe flag, we ensure we save the right activity

Instantiate some key variables:

const teamsChannel = '19:8d60061c3d10xxxxxxxxxxxxxxxx@thread.skype';
const serviceUrl = 'https://smba.trafficmanager.net/amer/';
  • Note: the easiest way to get these variables is to send a message from Teams to the bot while putting a breakpoint on the incoming activity
  • serviceUrl likely varies by geo region

Send the first activity and store the ID:

// This ensures that your bot can send to Teams
turnContext.activity.conversation.id = teamsChannel;
turnContext.activity.serviceUrl = serviceUrl;
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);

// Add the saveMe flag
yourActivity.channelData = { saveMe: true };

const response = await turnContext.sendActivity(yourActivity);
this.activityToUpdateId = response.id;
  • How you store that ID to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.

Update your saved activity:

// New data
const card2 = CardFactory.adaptiveCard(adaptiveCard2);

// Set the saved activity.id and new activity data (an adaptiveCard, in this example)
this.savedActivity.id = this.activityToUpdateId;
this.savedActivity.attachments = [card2];

Send the update:

await turnContext.updateActivity(this.savedActivity);
  • Note: you can update the activity with anything. I swapped out entirely different Adaptive Cards

Before:

enter image description here

After:

enter image description here

mdrichardson
  • 7,141
  • 1
  • 7
  • 21
  • I get an error "BotFrameworkAdapter.updateActivity(): missing serviceUrl" when I tried to implement the above method. The updateActivity methods expects the serviceUrl – Pratik Mathur Mar 15 '19 at 13:28
  • @PratikMathur Sorry about that. There was so much trial and error figuring this out, my code was a bit messy. I've edited my answer to account for this. You basically just need to `whateverActivity.serviceUrl = yourTeamsServiceUrl`. – mdrichardson Mar 15 '19 at 15:50
  • I still have the same problem since the updateActivity is expecting a context.activity object. Can you share the code snippet that worked for you? – Pratik Mathur Mar 15 '19 at 16:00
  • 1
    I've updated my answer. It appears that in my delirium of working on this late last night, I missed a crucial part. Here's [a gist of my actual code](https://gist.github.com/mdrichardson/f2fa1fe613709f2f0d83b85d51ca8a5f) – mdrichardson Mar 15 '19 at 16:50
  • I've made all the changes. However, now I get an error "Activity resulted into multiple skype activities". Any idea why we get this error? – Pratik Mathur Mar 15 '19 at 18:52
  • I got that one, too unless I did both 1) use a previous activity to build the new one, and 2) give the new activity the appropriate id. You're not still setting the activity.id manually, are you? – mdrichardson Mar 15 '19 at 19:16
0

I've tried this using the middleware but keep getting: "The bot is not part of the conversation roster". Question: My bot is updating a message that a user wrote, so do I need special permissions?

     let ActivityID = context.activity.conversation.id.split("=")[1];

        let updatedActivity: Partial<Activity> = {
            "id":  ActivityID, 
            "channelId": context.activity.channelId,
            "channelData": context.activity.channelData,
            "conversation": 
                            {
                              "name": "",
                              "id": context.activity.conversation.id,
                              "isGroup": context.activity.conversation.isGroup,
                              "conversationType": context.activity.conversation.conversationType,
                              "tenantId": context.activity.conversation.tenantId
                            },
            "type": "message",
            "text": "",
            "summary": "",
            "attachments": [ attachment ]
        }       await context.updateActivity(updatedActivity);