Microsoft Bot Framework — как добавлять вложения в FormFlow

Привет. В репозиторий Bot Builder SDK был добавлен коммит пользователя @pcostantini который даёт возможность добавления вложений при использовании возможностей FormFlow. Ниже я покажу как можно использовать новые возможности и что было добавлено.

Как это работает. В самом простом виде, в вашу модель необходимо добавить новый элемент типа AwaitableAttachment. Например, имея модель:

[Serializable]
public class ModelWithAttachments
{
    public AwaitableAttachment File;
}

Используя FormFlow для данной модели:

Chain.From(() => FormDialog.FromForm(BuildForm)); 

Сразу получим возможность добавления вложения:

Добавление вложений в FormFlow используя AwaitableAttachment

Если вы хотите задать допустимый тип для принимаемого файла, то можно воспользоваться атрибутом AttachmentContentTypeValidator

[Serializable]
public class ModelWithAttachments
{
    [AttachmentContentTypeValidator(ContentType = "png")]
    public AwaitableAttachment File;
}

После этого файл с неверным типом не даст загрузить, выдав предупреждение:

Валидация типа передаваемого файла в FormFlow

В текущей реализации можно задать проверку только одного типа. Сама проверка в коде выглядит следующим образом:

attachment.ContentType.ToLowerInvariant().Contains(this.ContentType.ToLowerInvariant()); 

На мой взгляд, конечно, не хватает возможности задать несколько возможных типов файлов. Но, думаю, скоро это добавится.

Можно обрабатывать сразу несколько файлов, для этого воспользовавшись интерфейсом IEnumerable.

[Serializable]
public class ModelWithAttachments
{
    [AttachmentContentTypeValidator(ContentType = "png")]
    public IEnumerable<AwaitableAttachment> Files;
}

Это позволит вам отправлять сразу несколько файлов:

Загрузка группы файлов с валидацией типа при работе с FormFlow

При работе с вложениями, можно использовать другие атрибуты из FormFlow, например Optional, сделав добавление файла не обязательным.

[Serializable]
public class ModelWithAttachments
{
    public string Name;

    [Optional]
    public IEnumerable<AwaitableAttachment> Files;
}

Теперь вы можете пропускать загрузку вложений в чат-бот:

Добавление необязательного вложения в FormFlow

При необходимости вы можете наследоваться от класса AwaitableAttachment и переопределять поведение 3 virtual методов:

public virtual string ProvideHelp<T>(IField<T> field) where T : class
{
    var help = string.Empty;
    foreach (var validator in this.GetValidators(field))
    {
        help += $"{Environment.NewLine}- {validator.ProvideHelp()}";
    }

    // TODO: if field.Optional then display hint/tip that it can be skipped with 'none', for example

    return help;
}

public virtual async Task<ValidateResult> ValidateAsync<T>(IField<T> field, T state) where T : class
{
    var result = new ValidateResult { IsValid = true, Value = this };

    var errorMessage = default(string);
    foreach (var validator in this.GetValidators(field))
    {
        var isValid = await validator.IsValidAsync(this.attachment, out errorMessage);
        if (!isValid)
        {
            result.IsValid = false;

            result.Feedback = result.Feedback ?? string.Empty;
            result.Feedback += $"{Environment.NewLine}{errorMessage}";
        }
    }

    return result;
}

protected virtual async Task<Stream> ResolveFromSourceAsync(Attachment source)
{
    if (!string.IsNullOrWhiteSpace(source.ContentUrl))
    {
        using (var client = new HttpClient())
        {
            if (MicrosoftAppCredentials.IsTrustedServiceUrl(source.ContentUrl))
            {
                await client.AddAPIAuthorization();
            }

            var stream = await client.GetStreamAsync(source.ContentUrl);
            var ms = new MemoryStream();
            stream.CopyTo(ms);
            ms.Position = 0;
            return ms;
        }
    }

    return null;
}

Для примера, создадим класс AwaitablePngAndJpg, который будет позволять загрузить только PNG и JPG изображения. Данный класс наследуем от класса AwaitableAttachment и перегружаем метод ValidateAsync

[Serializable]
public class AwaitablePngAndJpg : AwaitableAttachment
{
    public AwaitablePngAndJpg(Attachment source) : base(source) { }

    protected AwaitablePngAndJpg(SerializationInfo info, StreamingContext context) : base(info, context) { }

    public override async Task<ValidateResult> ValidateAsync<T>(IField<T> field, T state)
    {
        var result = await base.ValidateAsync(field, state);

        if (result.IsValid)
        {
            var isValidForMe = this.Attachment.ContentType.ToLowerInvariant().Contains("image/png") || Attachment.ContentType.ToLowerInvariant().Contains("image/jpeg");

            if (!isValidForMe)
            {
                result.IsValid = false;
                result.Feedback = $"Вы можете добавлять только .png или .jpg файлы. Некорректный файл '{this.Attachment.Name}'!";
            }
        }

        return result;
    }
}

Теперь можно использовать данный класс в нашей модели:

[Serializable]
public class ModelWithAttachments
{
    public AwaitablePngAndJpg File;
}

Поведение чат-бота при этом будет следующим:

Использования собственного типа для добавления вложений в FormFlow

FormFlow это отличный инструмент, который экономит много времени, позволяя создавать интерфейсы для заполнения необходимых моделей данных. С появлением типа AwaitableAttachment в FormFlow можно использовать вложения, что значительно расширяет область применения, ведь теперь по мимо обычных данных вы можете отправлять различные файлы. Данный класс легко кастомизируется, вы легко настроите его для использования в своём проекте, также вы можете настраивать работу с ним как и для других типов в FormFlow, задавая более подходящее имя, добавляя подсказки и прочее.

Сейчас данное изменение уже загружено в Nuget, вы можете его полноценно использовать в своих проектах.

Приятного программирования.

Добавить комментарий