Редактировать: Дрю отвечает правильно, но мой дает другое потенциальное решение.Вы можете найти больше информации здесь: Управление государством .В частности:
Состояние пользователя доступно в любой ход, когда бот общается с этим пользователем на этом канале, независимо от диалога. Состояние разговора доступно в любой ход в конкретном разговоре, независимо от пользователя.(т. е. групповые разговоры). Состояние приватного разговора ограничивается как конкретным разговором, так и конкретным пользователем.
Подсказка
Состояние пользователя и разговора ограничено каналом.Один и тот же человек, использующий разные каналы для доступа к вашему боту, выглядит как разные пользователи, по одному для каждого канала, и у каждого из них свое состояние пользователя.
Дополнительное решение
Это решение лучше всего подходит дляесли вы можете указать from Id
, но не можете гарантировать, что conversation Id
останется прежним (см. ниже, под Gotchas).
Вы можете сохранить, на каком шаге находится пользователь в его UserState
.
BasicBot
BasicBot делает это со своим GreetingState
классом.
Из своего GreetingDialog
:
На первом шаге инициализируется GreetingState
, который отслеживает, как далеко в диалоге находится пользователь, видя, какие пользовательские переменные уже установлены:
private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
if (greetingState == null)
{
var greetingStateOpt = stepContext.Options as GreetingState;
if (greetingStateOpt != null)
{
await UserProfileAccessor.SetAsync(stepContext.Context, greetingStateOpt);
}
else
{
await UserProfileAccessor.SetAsync(stepContext.Context, new GreetingState());
}
}
return await stepContext.NextAsync();
}
И затем в каждомшаг, он загружает GreetingState
:
var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context);
и проверяет, был ли шаг уже выполнен с чем-то вроде:
if (greetingState != null && !string.IsNullOrWhiteSpace(greetingState.Name) && !string.IsNullOrWhiteSpace(greetingState.City))
Если нет greetingState
или .Name
или .City
существует, он запрашивает их, и если они уже заполнены, он переходит к:
return await stepContext.NextAsync();
На каждом шаге он сохраняет в GreetingState
что-то вроде:
greetingState.Name = char.ToUpper(lowerCaseName[0]) + lowerCaseName.Substring(1);
await UserProfileAccessor.SetAsync(stepContext.Context, greetingState);
Упрощение для вашего варианта использования
Для вас, если вы нене нужно сохранять информацию о пользователе, вы можете создать простой Step
класс:
{
/// <summary>
/// User state properties for Waterfall Step.
/// </summary>
public class Step
{
public string StepNumber { get; set; }
}
}
сделать первый шаг вашего WaterfallDialog:
private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var StepState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
if (StepState == null)
{
var StepStateOpt = stepContext.Options as StepState;
if (StepStateOpt != null)
{
await UserProfileAccessor.SetAsync(stepContext.Context, StepStateOpt );
}
else
{
await UserProfileAccessor.SetAsync(stepContext.Context, new StepState());
}
}
return await stepContext.NextAsync();
}
На каждом шаге загружатьcurrent Step
:
var stepState = await UserProfileAccessor.GetAsync(stepContext.Context);
Проверьте, не прошел ли уже текущий шаг:
if (stepState.StepNumber <= 2)
{
// ...do stuff
// Save that user has completed step
stepState.StepNumber++;
await UserProfileAccessor.SetAsync(stepContext.Context, stepState);
}
else
{
return await stepContext.NextAsync();
}
Gotchas
Пара важных вещей, на которые стоит обратить вниманиедля:
UserState сохраняется только для тех же from ID
и channel ID
.Убедитесь, что у пользователя, который выходит посреди водопада, есть тот же from ID
при повторном входе в него и что он повторно вводит его с того же канала .Это не значение по умолчанию для эмулятора - в эмуляторе при перезапуске сеанса создается новый from ID
.(Примечание. Считайте, что from ID
является синонимом User ID
. Оно происходит только от Activity.From.Id
)
ConversationState сохраняется только для тех же conversation ID
и channel ID
.Постоянство conversation ID
в пределах канала зависит от канала.
Дополнительная информация о различных идентификаторах: Поля идентификатора в Bot Framework .