I am trying to implement proactive messages on bot framework v4, It works, but only with the string on the BotCallback function, I need to pass custom text but ContinueConversationAsync doesnt seems to allow it
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, BotCallback, default(CancellationToken));
return true;
}
private ConversationReference ObtenerReferenciaConversacion(string conversationId)
{
return new ConversationReferenceModulo().ObtenerConversationReference(conversationId);
}
public MensajeroDefaultModulo(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
{
_adapter = adapter;
_appId = configuration["MicrosoftAppId"];
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
await turnContext.SendActivityAsync("proactive hello", cancellationToken: cancellationToken);
}
You can make a wrapper around BotCallback using LINQ approach. But personally i do not understand the idea of such delegates without any EventArgs.
[HttpPost]
public async Task<IActionResult> Post([FromBody] string messageText)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => await BotCallback(messageText, context, token), default(CancellationToken));
}
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
private async Task BotCallback(string message, ITurnContext turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync(message, cancellationToken: cancellationToken);
}
What I understand you are wanting to do is to pass a value to the BotCallback that can be sent back via the method SendActivityAsync.
To do this you can use a lambda expression instead of calling the BotCallback.
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var yourVariable = "blah";
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, async (context, token) => {
await turnContext.SendActivityAsync("proactive hello " + yourVariable, cancellationToken: cancellationToken);
}, default(CancellationToken));
return true;
}
This comes from this reference to a proactive messaging sample here: https://github.com/microsoft/botbuilder-dotnet/issues/787
For Lambda expressions see Lambda Expressions (C# Programming Guide)
A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.
All lambda expressions use the lambda operator =>, which is read as "goes to". The left side of the lambda operator specifies the input parameters (if any) and the right side holds the expression or statement block. The lambda expression x => x * x is read "x goes to x times x."
Updated with API controller example
The SendProactiveMessage needs to be in a Controller in your Bot project, for example:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
namespace ProactiveBot.Controllers
{
[Route("api/notify")]
[ApiController]
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"];
// If the channel is the Emulator, and authentication is not in use,
// the AppId will be null. We generate a random AppId for this case only.
// This is not required for production, since the AppId will have a value.
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
public async Task<IActionResult> Get([FromQuery(Name = "taskID")] int taskID)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => {
await context.SendActivityAsync("proactive task notification for TaskID: " + taskID.ToString());
}, default(CancellationToken));
}
// Let the caller know proactive messages have been sent
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1><p>" + taskID.ToString() + "</p></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
}
}
You can then get access to the context and token through the injected adapter. The above code is largely from the current Proactive Message sample code
So I can then call this API eg. http://localhost:3988/api/notify?taskID=5678 passing in the taskID parameter (in my case) which is then sent through to the user via ContinueConversationAsync.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With