Search code examples
c#botframework

Botframework v4: Send Typing indicaor


Hello I am sending a typing indicator like this to every turn. It's working but is there a better or more efficient way to do this? I am sending it several times because if i only send it once, the typing indicator will not show.

   AddStep(async (stepContext, cancellationToken) =>
            {
                var typingMsg = stepContext.Context.Activity.CreateReply();
                typingMsg.Type = ActivityTypes.Typing;
                typingMsg.Text = null;
                await stepContext.Context.SendActivityAsync(typingMsg);
                await stepContext.Context.SendActivityAsync(typingMsg);
                await stepContext.Context.SendActivityAsync(typingMsg);
                await stepContext.Context.SendActivityAsync(typingMsg);
                await stepContext.Context.SendActivityAsync(typingMsg);

                await stepContext.Context.SendActivityAsync(MessageFactory.Text($"I have some important questions to ask you."), cancellationToken: cancellationToken);
                await stepContext.Context.SendActivityAsync(MessageFactory.Text($"This will not take long."), cancellationToken: cancellationToken);
                return await stepContext.NextAsync(cancellationToken: cancellationToken);
            });

EDIT: Is it okay to do this on turns that i want to add a 2 seconds typing indicator? Because some turns have long dialog and i want it to feel natural. And should i add it to all turns without but without the task.delay? Will it not impact the passing of results from the previous step?

        AddStep(async (stepContext, cancellationToken) =>
        {
            var typingMsg = stepContext.Context.Activity.CreateReply();
            typingMsg.Type = ActivityTypes.Typing;
            typingMsg.Text = null;
            await stepContext.Context.SendActivityAsync(typingMsg);
            await Task.Delay(2000);

            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"I have some important questions to ask you."), cancellationToken: cancellationToken);
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"This will not take long."), cancellationToken: cancellationToken);
            return await stepContext.NextAsync(cancellationToken: cancellationToken);
        });

Solution

  • You should not have to send multiple typing activities like that. If the code you included above is representative of your exact code right now, you wouldn't really see the typing indicator because you're effectively sending a message that replaces the indicator immediately. The reason you might see it after you've added multiple in a row is because it delays the clearing of the indicator that happens when you finally send your first message.

    To test this out, just revert to sending a single typing activity and then emulate the time it would take to do work by putting an await Task.Delay(3000); in place to emulate a small pause. This should give the client time to show the typing indicator before the delay ends and your message is then sent through.

    UPDATE

    Yes, awaiting a Task::Delay is fine if you want to introduce some kind of natural pause into your responses. A pattern we've seen is to do this with a piece of middleware that uses an algorithm based on character count to synthesize the delay.

    UPDATE2

    I don't know how I forgot about this, but there is actually a piece of middleware shipped in the box called the ShowTypingMiddleware that includes functionality like this based on how long the turn is taking before response activities are sent out. I thought it was only shipped it as an example somewhere, but I just came across it again this morning and wanted to make sure to update this answer. It's not exactly the same as what I described above, but it might be the behavior some people are looking for.