Search code examples
c#botframeworkattachmentchatbotsample

Accepting attachments on a waterfall dialog and storing them locally in bot framework v4


I was trying to add a functionality to take input-attachments from the user, basically trying to merge the handling-attachments bot sample from bot framework and my custom waterfall dialog .

But how do you access iturncontext functions in the waterfall dialog? . Below is a explanation of my code.

One of my waterfall step :

private async Task<DialogTurnResult> DescStepAsync2(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{


    stepContext.Values["Title"] = (string)stepContext.Result;
    await stepContext.Context.SendActivityAsync(MessageFactory.Text("upload a image"), cancellationToken);

    var activity = stepContext.Context.Activity;
    if (activity.Attachments != null && activity.Attachments.Any())
    {

        Activity reply = (Activity)HandleIncomingAttachment(stepContext.Context.Activity);
        return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = reply }, cancellationToken);
    }
    else
    {
        var reply = MessageFactory.Text("else image condition thrown");
        //  reply.Attachments.Add(Cards.GetHeroCard().ToAttachment());
        return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = reply }, cancellationToken);
    }
}

Here is the HandleIncomingAttachment function which i borrowed from bot builder samples linked above .

private static IMessageActivity HandleIncomingAttachment(IMessageActivity activity)
{
    string replyText = string.Empty;
    foreach (var file in activity.Attachments)
    {
        // Determine where the file is hosted.
        var remoteFileUrl = file.ContentUrl;

        // Save the attachment to the system temp directory.
        var localFileName = Path.Combine(Path.GetTempPath(), file.Name);

        // Download the actual attachment
        using (var webClient = new WebClient())
        {
            webClient.DownloadFile(remoteFileUrl, localFileName);
        }

        replyText += $"Attachment \"{file.Name}\"" +
                     $" has been received and saved to \"{localFileName}\"\r\n";
    }

    return MessageFactory.Text(replyText);
}

Here is the transcript of the conversation: Transcript bot framework

EDIT: i have edited my code to this , it still doesnt wait for me to upload a attachment .just finishes the step .

private async Task<DialogTurnResult> DescStepAsync2(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    stepContext.Values["Desc"] = (string)stepContext.Result;
    var reply = (Activity)ProcessInput(stepContext.Context);
    return await stepContext.PromptAsync(nameof(AttachmentPrompt), new PromptOptions { Prompt = reply }, cancellationToken);
}

process input function :

private static IMessageActivity ProcessInput(ITurnContext turnContext)
{
    var activity = turnContext.Activity;
    IMessageActivity reply = null;

    if (activity.Attachments != null && activity.Attachments.Any())
    {
        // We know the user is sending an attachment as there is at least one item .
        // in the Attachments list.
        reply = HandleIncomingAttachment(activity);
    }
    else
    {
        reply = MessageFactory.Text("No attachement detected ");
        // Send at attachment to the user.              
    }
    return reply;
}

Solution

  • so i figured this out , thanks to this post: github.com/microsoft/botframework-sdk/issues/5312

    how my code looks now :

    declaring a attachment prompt:

     public class CancelDialog : ComponentDialog
    {
    
        private static string attachmentPromptId = $"{nameof(CancelDialog)}_attachmentPrompt";
        public CancelDialog()
            : base(nameof(CancelDialog))
        {
    
            // This array defines how the Waterfall will execute.
            var waterfallSteps = new WaterfallStep[]
            {
                TitleStepAsync,
                DescStepAsync,
              //  AskForAttachmentStepAsync,
                UploadAttachmentAsync,
                UploadCodeAsync,
                SummaryStepAsync,
    
            };
    
            // Add named dialogs to the DialogSet. These names are saved in the dialog state.
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>), AgePromptValidatorAsync));
            AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
            AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
            AddDialog(new AttachmentPrompt(attachmentPromptId));
    

    ask for attachment prompt in the waterfall :

    private async Task<DialogTurnResult> UploadAttachmentAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
    
            stepContext.Values["desc"] = (string)stepContext.Result;
         //   if ((bool)stepContext.Result)
            {
                return await stepContext.PromptAsync(
            attachmentPromptId,
            new PromptOptions
            {
                Prompt = MessageFactory.Text($"Can you upload a file?"),
            });
            }
            //else
            //{
            //    return await stepContext.NextAsync(-1, cancellationToken);
            //}
    
        }
    

    processing the file and storing it :

    private async Task<DialogTurnResult> UploadCodeAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            List<Attachment> attachments = (List<Attachment>)stepContext.Result;
            string replyText = string.Empty;
            foreach (var file in attachments)
            {
                // Determine where the file is hosted.
                var remoteFileUrl = file.ContentUrl;
    
                // Save the attachment to the system temp directory.
                var localFileName = Path.Combine(Path.GetTempPath(), file.Name);
    
                // Download the actual attachment
                using (var webClient = new WebClient())
                {
                    webClient.DownloadFile(remoteFileUrl, localFileName);
                }
    
                replyText += $"Attachment \"{file.Name}\"" +
                             $" has been received and saved to \"{localFileName}\"\r\n";
            }}
    

    hope you get a idea . thank you @Kyle and @Michael