Reputation: 103
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
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