Morales1235
Morales1235

Reputation: 103

TeamsBot doesn't have conversation references

I have Teams Bot with a code pretty much like example of proactive messages from ms docs. When I run bot locally - either with Bot Emulator, or Ngrok + Teams Bot registered in Azure, notifications work as expected. When I publish my bot to Azure (as an app service), it does not have conversation references to send proactive message.
My notify controller looks the same as the sample, I just added printing how many conv references are there.

public class NotifyController : ControllerBase {
    private readonly IBotFrameworkHttpAdapter _adapter;
    private readonly string _appId;
    private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;

    public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary<string, ConversationReference> conversationReferences) {
        _adapter = adapter;
        _conversationReferences = conversationReferences;
        _appId = configuration["MicrosoftAppId"] ?? "<my-app-id>";
    }

    public async Task<IActionResult> Get() {
        foreach (var conversationReference in _conversationReferences.Values) {
            Debug.WriteLine($"awaiting callback from convdId({conversationReference.Conversation.Id}) from user {conversationReference.User.Name}{conversationReference.User.Id}");
            await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, BotCallback, default);
        }

        return new ContentResult() {
            Content = $"<html><body><h1>Proactive messages have been sent to {_conversationReferences.Count} conversations.</h1></body></html>",
            ContentType = "text/html",
            StatusCode = (int)HttpStatusCode.OK,
        };
    }

    private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken) {
        await turnContext.SendActivityAsync("proactive hello", cancellationToken: cancellationToken);
    }
}

ConfigureServices in Startup.cs:

public void ConfigureServices(IServiceCollection services) {
        services.AddHttpClient().AddControllers().AddNewtonsoftJson();
        services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
        services.AddSingleton<IStorage, MemoryStorage>();
        services.AddSingleton<UserState>();
        services.AddSingleton<ConversationState>();
        services.AddSingleton<MainDialog>();
        services.AddTransient<IBot, DialogBot<MainDialog>>();
        services.AddSingleton<ConcurrentDictionary<string, ConversationReference>>();
    }

Also in Bot code there are functions (does not matter of the commented function is uncommented, behavior is the same):

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) {
    await base.OnTurnAsync(turnContext, cancellationToken);
    //AddConversationReference(turnContext.Activity as Activity);
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
    ...
private void AddConversationReference(Activity activity) {
    var conversationReference = activity.GetConversationReference();
    ConversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}

protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) {
    AddConversationReference(turnContext.Activity as Activity);
    return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
}

I don't know where the references are set to investigate why they are not set :/ running locally, I can see proper number of conversations.
I also added logs to see if comments of Hilton Giesenow are applicable; looks like conversation references are not set, so I don't have any data to save. Sometimes (I don't know under what circumstances) OnTurnAsync is called and then there is data in conversation reference.

Upvotes: 0

Views: 236

Answers (1)

Hilton Giesenow
Hilton Giesenow

Reputation: 10804

What you're using is just a sample, as a result it's there to teach important concepts, but not meant to be used 100% as is in production. In the sample, for instance, it stores all the data for the application in memory (notice this line: services.AddSingleton<IStorage, MemoryStorage>();). That means that the information being stored, like the conversation references, are only available while the application is running, and only on the single computer it's running on.

For a production scenario, you need to be saving these conversation references to a "durable" storage (one that lasts as long as you need), for example a database or a file storage of some sort. Then you can retrieve it from that storage whenever you need it, e.g. to send a proactive message.

On a related note, when you store something in a permanent location, you generally need to "key" it, which means to store it in a way you can uniquely find each record as needed in the future. In the case of a Teams bot, using the AadObjectId for the user is a good starting point for this, as it's unique to every single user.

Upvotes: 1

Related Questions