-
Notifications
You must be signed in to change notification settings - Fork 0
Waiting for user interaction
Framework contains Sessions support, which serves for asynchronously waiting for user interaction (sending message, clicking the inline button).
For example, your bot should do this:
- Bot sends "Hello, please write your name:" and waits for user message in the current chat, or anywhere else (you could specify this at the checking time).
- User sends "John".
- Bot receives message, and writes "John" into the variable.
How to implement that in the simple way?
Firstly, you should add session handler to the start of the processing pipeline and register ISessionManager
in the ContainerBuilder
, and your code should be like that:
All classes and definitions are taken from the Getting Started page.
Registering session manager:
containerBuilder.RegisterSessionManager();
Configuring processing pipeline:
botBuilder.WithPositionedUpdateEntityProcessorBuilder<Message>(
MessagesEntityProcessorId,
UpdateType.Message,
updateEntityProcessorBuilder => updateEntityProcessorBuilder
.WithSessionRequestUpdateEntityHandler(new MessageEntityHandlerAttribute(0))
.WithUpdateEntityHandlersFromType(typeof(CheckMessageTextForHelloHandlerSource));
Then, lets change some code in our CheckMessageTextForHelloHandler message handler:
[MessageEntityHandlerAttribute(position: 1)]
public async Task<bool> CheckMessageTextForHelloHandler(IHandlerContext<Message> messageHandlerContext,
ITextMatcherService textMatcherService)
{
ISession<Message>
session = messageHandlerContext.Session(); //Retrieving session, related to the current user and update type.
int maxAttempts = 10; //Set max attempts for passing our "check"
ISessionUpdateEntityRequestResult<Message> messageRequestResult = await session.RequestEntityAsync(
new SessionUpdateEntityRequestOptions<Message>()
{
Action = async state =>
{
int availableAttempts = maxAttempts - state.CurrentAttempt;
await messageHandlerContext.BotClient.SendTextMessageAsync(messageHandlerContext.Entity.Chat,
$"Hello, you have {availableAttempts} attempts left, please write your name:");
},
Matcher = state => textMatcherService.CheckThatMessageTextMatchSomeCriteria(state.LastEntity.Text),
Attempts = maxAttempts
});
if (messageRequestResult.IsSuccess)
{
Message receivedMessage = messageRequestResult.Entity;
await messageHandlerContext.BotClient.SendTextMessageAsync(messageHandlerContext.Entity.Chat,
$"Congrats! You've passed the check! Your text was: {receivedMessage.Text}.");
}
else
{
await messageHandlerContext.BotClient.SendTextMessageAsync(messageHandlerContext.Entity.Chat,
"Fail! Oops, seems like your messages didn't contain required text for passing the check :(");
}
return messageRequestResult.IsSuccess;
}
What does this code do?
- Bot sends "Hello, you have 10 attempts left, please write your name:" at the start.
- Asynchronously waits for the user message which should contain "hello" text.
- If received message contains "hello", returns this message to the our handler (
messageRequestResult
will contain this message in theEntity
property). Otherwise, performs steps 1-3 again, until condition would betrue
, or until current attempt would equal to max attemps count. - Depending on the message request result, sends message to the user which indicates our "check" success or fail.
And, how our sessions works under the hood?
Default implementation of ISession<TEntity>
implies using System.Threading.Channels package, which implies Producer-Consumer pattern using.
Basically, when RequestEntityAsync
was called, session's flag (IsUpdateEntityRequested
), which indicates, should incoming TEntity
object be routed to our session, and then to our handler or command where RequestEntityAsync
was called, will be set to true
, and when our session handler (which we previously added by calling WithSessionRequestUpdateEntityHandler(...)
method) will be invoked, it will see, that message for the current user and update type, should be routed into our session.
Routing is performed in that way:
-
RequestEntityAsync
invokesAction
, then waits until entity channel will have at least one object. - When entity was successfully wrote to the channel, waiting will be completed and
Matcher
will be called, to check that entity matches all requirements. - If check was successfull, returns entity. Otherwise, performs steps described above until
Matcher
result will betrue
, or attempts count would exceed max attempts count.
Sessions using also shown in the example here.