Привет. В репозиторий Bot Builder SDK был добавлен коммит пользователя @pcostantini который даёт возможность добавления вложений при использовании возможностей FormFlow. Ниже я покажу как можно использовать новые возможности и что было добавлено.
Как это работает. В самом простом виде, в вашу модель необходимо добавить новый элемент типа AwaitableAttachment
. Например, имея модель:
[Serializable]
public class ModelWithAttachments
{
public AwaitableAttachment File;
}
Используя FormFlow для данной модели:
Chain.From(() => FormDialog.FromForm(BuildForm));
Сразу получим возможность добавления вложения:
Если вы хотите задать допустимый тип для принимаемого файла, то можно воспользоваться атрибутом AttachmentContentTypeValidator
[Serializable]
public class ModelWithAttachments
{
[AttachmentContentTypeValidator(ContentType = "png")]
public AwaitableAttachment File;
}
После этого файл с неверным типом не даст загрузить, выдав предупреждение:
В текущей реализации можно задать проверку только одного типа. Сама проверка в коде выглядит следующим образом:
attachment.ContentType.ToLowerInvariant().Contains(this.ContentType.ToLowerInvariant());
На мой взгляд, конечно, не хватает возможности задать несколько возможных типов файлов. Но, думаю, скоро это добавится.
Можно обрабатывать сразу несколько файлов, для этого воспользовавшись интерфейсом IEnumerable
.
[Serializable]
public class ModelWithAttachments
{
[AttachmentContentTypeValidator(ContentType = "png")]
public IEnumerable<AwaitableAttachment> Files;
}
Это позволит вам отправлять сразу несколько файлов:
При работе с вложениями, можно использовать другие атрибуты из FormFlow, например Optional
, сделав добавление файла не обязательным.
[Serializable]
public class ModelWithAttachments
{
public string Name;
[Optional]
public IEnumerable<AwaitableAttachment> Files;
}
Теперь вы можете пропускать загрузку вложений в чат-бот:
При необходимости вы можете наследоваться от класса 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 это отличный инструмент, который экономит много времени, позволяя создавать интерфейсы для заполнения необходимых моделей данных. С появлением типа AwaitableAttachment
в FormFlow можно использовать вложения, что значительно расширяет область применения, ведь теперь по мимо обычных данных вы можете отправлять различные файлы. Данный класс легко кастомизируется, вы легко настроите его для использования в своём проекте, также вы можете настраивать работу с ним как и для других типов в FormFlow, задавая более подходящее имя, добавляя подсказки и прочее.
Сейчас данное изменение уже загружено в Nuget, вы можете его полноценно использовать в своих проектах.
Приятного программирования.