SOLVED

Teams Bot - user conversation objects are becoming stale, breaking the ability to send proactive msg

Copper Contributor

Hi, we develop a 3rd party app on the MS Teams store, which includes functionality to send proactive messages to users. The outline of the code we are using is as follows:

Our deployed bot listens to new users joining team where the bot is installed, using the 

onTeamsMembersAddedEvent hook:

 

 

class WelcomeBot extends TeamsActivityHandler {
  this.onTeamsMembersAddedEvent(async (membersAdded, teamInfo, context, next) => {
    const members = [];
    do {
      var pagedMembers = await TeamsInfo.getPagedMembers(context, 100, continuationToken);
      continuationToken = pagedMembers.continuationToken;
      members.push(...pagedMembers);
    }
    await next();
  } while (continuationToken !== undefined);
  ...
  members.forEach(async (teamMember) => {
      const ref = TurnContext.getConversationReference(context.activity);
      ref.user = teamMember;
      await context.adapter.createConversation(ref,
        async (t1) => {
          const conversationRef = TurnContext.getConversationReference(t1.activity);
          // conversationRef is saved to the database for this user
          if (statement that checks if this is a new user) {
            await t1.adapter.continueConversation(conversationRef, async (t2) => {
              await this.sendWelcomeCard(t2); // sends a welcome card for new users
            });
          }
      );
    });
}

 

 


To summarize the code above, for each member we detect in the team this bot is deployed to, we get their conversation reference (the entire object), and save it to the database for that user.

To send proactive messages in the future, we use code like this:

 

 

// using conversation = the same conversation JSON object that was saved to the database
BotConnector.MicrosoftAppCredentials.trustServiceUrl(conversation.serviceUrl);
adapter.continueConversation(conversation, async (t2) => {
  const card = BotBuilder.CardFactory.heroCard(
    title,
    message,
    [],
    actions
  );
  await t2.sendActivity({ attachments: [card] });
}

 

 

 
Sometimes, this works for users for a long time (or forever), meaning the conversation object we saved to the DB is still the correct one we can pull up and use to send proactive messages. However, for other users (including myself), after a few days of use this saved conversation object is no longer valid to send messages to, and the users stop getting proactive messages.

There is a workaround we can do, which involves removing and re-adding our app to personal scope, or adding new users to the channel where the app/bot is installed, which runs the 

onTeamsMembersAddedEvent hook again, and "refreshes" every user's conversation reference. This makes proactive messages work again, but only temporarily. The only way to diagnose this issue is to notice that you are missing proactive messages in the first place, which is a really bad UX for our users which expect proactive messages to always come through.

My question is, is there another way to structure or layout the code to avoid trying to send messages to stale conversation objects, without having to go and trigger the bot 
onTeamsMembersAddedEvent manually to refresh them? Maybe we don't need to be storing the whole conversation reference, and before sending out a proactive message there is another API or library call we can do to using Microsoft API to make sure we are getting the correct conversation reference for a user? Or any other suggestions on how to improve this so our users always correctly get proactive messages - this is a pretty big failure on our app if we are not able to deliver this.

Thanks
6 Replies
@AlexK480 - Thanks for reporting your issue.
We will check this at our end and will get back to you.

@AlexK480 - Microsoft Teams supports proactive messaging, which allows your bot to initiate conversations with users without waiting for them to send a message first. By using proactive messages, you can ensure that your bot always has the latest conversation context. This way, you won't need to rely solely on responding to incoming messages.

Reference Document-https://learn.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bot-conversations/bots-co...

We are checking with the internal team as well and let you know the update once we have any.

Thanks, please let us know if you hear anything back from your internal teams. Just to clarify, we know what proactive messaging is and are actively using it in our app. The problem is that the proactive messaging occasionally breaks, with a workaround to restore it, but it still breaks. If it helps your teams, I have an example of a conversation reference (JSON object that I would need to share over a private channel) that was saved and is no longer valid to send proactive messages to.
@AlexK480-Sure, you can send the information through mail.
mail ID-microsoftteamsdev@microsoft.com
@AlexK480-Did you get a chance to send the details to the above email?
best response confirmed by Sayali-MSFT (Microsoft)
Solution
Hey, we ended up working through the issue on our end - turns out the conversations were not stale after all, it was an issue with multiple tenant IDs. So you can close off the issue, the conversation objects themselves were still valid.

As a side note, if this issue comes up for us in the future or anyone else, there is a better approach than our initial approach we tested during debugging this issue. You can save just the conversation, user, bot ids and serviceUrl (instead of the whole conversation object), and get a fresh conversation reference prior to a proactive message using BotBuilder.TurnContext.getConversationReference(conversationRefData);
1 best response

Accepted Solutions
best response confirmed by Sayali-MSFT (Microsoft)
Solution
Hey, we ended up working through the issue on our end - turns out the conversations were not stale after all, it was an issue with multiple tenant IDs. So you can close off the issue, the conversation objects themselves were still valid.

As a side note, if this issue comes up for us in the future or anyone else, there is a better approach than our initial approach we tested during debugging this issue. You can save just the conversation, user, bot ids and serviceUrl (instead of the whole conversation object), and get a fresh conversation reference prior to a proactive message using BotBuilder.TurnContext.getConversationReference(conversationRefData);

View solution in original post