Issue in attachment upload in BOT emulator of Bot framework - bots

I am uploading an attachment in BOT emulator, after uploading an attachment I am converting it to base64, to pass it to our service.
I pick this attachment from path D:\Images\MobileRequest.PNG, but after uploading it to BOT app it shows the path of attachment as http://127.0.0.1:44185/v3/attachments/ne7djbemc9f40bifi/views/original/MobileRequest.PNG, as the image is not available on this path, So while converting the image to base64, it throws an error as "URI formats are not supported.".
How to get actual physical path i.e "D:\Images\MobileRequest.PNG" in BOT app.
Below is code from my BOT app
var dialog = new PromptDialog.PromptAttachment("Please attach screenshot ", "Sorry, I didn't get the attachment. Try again please.", 2);
context.Call(dialog, afterUpload);
private async Task afterUpload(IDialogContext context, IAwaitable<IEnumerable<Attachment>> result)
{
IEnumerable<Attachment> attach = await result;
string filePath = attach.FirstOrDefault().ContentUrl + "/" + attach.FirstOrDefault().Name;
context.UserData.SetValue("filePath", filePath);
}
string filePath = string.Empty;
context.UserData.TryGetValue("filePath", out filePath);
using (System.Drawing.Image image = System.Drawing.Image.FromFile(filePath))
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
byte[] imageBytes = m.ToArray();
attach1 = Convert.ToBase64String(imageBytes);
}
}

Your bot will be deployed so you will not have access to local files.
You can easily convert your image located at a URL by doing the following:
using (var client = new HttpClient())
{
var bytes = await client.GetByteArrayAsync(imageUrl);
var imageInBase64String = "image/jpeg;base64," + Convert.ToBase64String(bytes);
// Do what you want with your converted image
}

Related

Azure Text to Speech (Cognitive Services) in web app - how to stop it from outputting audio?

I'm using Azure Cognitive Services for Text to Speech in a web app.
I return the bytes to the browser and it works great, however on the server (or local machine) the speechSynthesizer.SpeakTextAsync(inp) line outputs the audio to the speaker.
Is there a way to turn this off, since this runs on a web server (and even if I ignore it, there's the delay while it outputs audio before sending back the data)
Here's my code ...
var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
speechConfig.SpeechSynthesisVoiceName = "fa-IR-FaridNeural";
speechConfig.OutputFormat = OutputFormat.Detailed;
using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
{
// todo - how to disable it saying it here?
var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(inp);
return Convert.ToBase64String(speechSynthesisResult.AudioData);
}
What you can do is add an audioconfig to the speechSynthesizer.
In this audioconfig object you can specify a file path to a .wav file which already exist on the server.
Whenever you run speaktextasyn instead of a speaker it will redirect the data to the .wav file.
This audio file you can read and perform your logic later.
Just add the following code before creating the speechSynthesizer object.
var audioconfig = AudioConfig.FromWavFileOutput(filepath);
here filepath is a location of the .wav file as a string.
Complete code :
string filepath = "<file path> " ;
var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
var audioconfig = AudioConfig.FromWavFileOutput(filepath);
speechConfig.SpeechSynthesisVoiceName = "fa-IR-FaridNeural";
speechConfig.OutputFormat = OutputFormat.Detailed;
using (var speechSynthesizer = new SpeechSynthesizer(speechConfig, audioconfig))
{
// todo - how to disable it saying it here?
var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(inp);
return Convert.ToBase64String(speechSynthesisResult.AudioData);
}

Error trying to attach an image to an Azure DevOps wiki page

I have successfully used the Azure DevOps API to create multiple wiki pages, via a standalone C# desktop app. Now I'm trying to attach an image (currently stored locally) to the wiki (as per https://learn.microsoft.com/en-us/rest/api/azure/devops/wiki/attachments/create?view=azure-devops-rest-6.0), but get an error
The wiki attachment creation failed with message : The input is not a
valid Base-64 string as it contains a non-base 64 character, more than
two padding characters, or an illegal character among the padding
characters.
This is the code that I use to read the image file and convert it to a Base64 string - is this correct?
string base64String = null;
string img = File.ReadAllText(att.Value);
byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(img);
base64String= Convert.ToBase64String(byteCredentials);
Then I create the "content" for the API call as
string data = #"{""content"": """ + base64String + #"""}";
and run the API call
string url = "https://dev.azure.com/{organization}/{project}/_apis/wiki/wikis/{wikiIdentifier}/attachments?name=Image.png&api-version=6.0";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/octet-stream";
request.Method = "PUT";
request.Proxy.Credentials = CredentialCache.DefaultCredentials;
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{1}", "AzurePAT"))));
if (data != null)
{
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = string.Empty;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
Can anyone see anything wrong with any of this? The concept of setting up a "content" JSON with an encoded Base64 string doesn't seem to be documented, so have I done it correctly?
Any help or advice gladly appreciated, thanks
An image file does not contain text, it is a binary file, calling File.ReadAllText probably messes up the encoding. Try:
var img = File.ReadAllBytes(att.Value);
var base64String = Convert.ToBase64String(img);
In addition, the request expects the body to just be a string. You are passing JSON. Your code should look like this:
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(base64String);
}

Copying files from FTP to Azure Blob Storage

I have created my FTP (ftp://xyz.in) with user id and credentials.
I have created an asp.net core API application that will copy files from FTP to Azure blob storage.
I have my API solution placed in C://Test2/Test2 folder.
Now below is my code :
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp:/xyz.in");
request.Method = WebRequestMethods.Ftp.UploadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential("pqr#efg.com", "lmn");
// Copy the contents of the file to the request stream.
byte[] fileContents;
// Getting error in below line.
using (StreamReader sourceStream = new StreamReader("ftp://xyz.in/abc.txt"))
{
fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
}
request.ContentLength = fileContents.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(fileContents, 0, fileContents.Length);
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
Console.WriteLine($"Upload File Complete, status {response.StatusDescription}");
}
But on line
using (StreamReader sourceStream = new StreamReader("ftp://xyz.in/abc.txt"))
I am getting error : System.IO.IOException: 'The filename, directory name, or volume label syntax is incorrect : 'C:\Test2\Test2\ftp:\xyz.in\abc.txt''
I am not able to understand from where does 'C:\Test2\Test2' string gets append to my FTP.
Test2 is a folder where my .Net Core application is placed.
StreamReader() doesn't take a URL/URI, it takes a file path on your local system: (read the doco):
https://learn.microsoft.com/en-us/dotnet/api/system.io.streamreader.-ctor?view=net-5.0
StreamReader is interpurting the string you've supplied as a filename ("ftp://xyz.in/abc.txt"), and it's looking for it in the current running folder "C:\Test2\Test2". If your string was "abc.txt", it would look for a file called "abc.txt" in the current folder, e.g. C:\Test2\Test2\abc.txt.
What you want is to get the file using WebClient or something similar:
WebClient request = new WebClient();
string url = "ftp://xyz.in/abc.txt";
request.Credentials = new NetworkCredential("username", "password");
try
{
byte[] fileContents = request.DownloadData(url);
// Do Something...
}

Docusign - Error opening PDF downloaded through the Rest API

With the REST api, I am trying to get the documents out of a completed envelope. My header is using X-DocuSign-Authentication.
EnvelopesApi ap = new EnvelopesApi();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
EnvelopeDocumentsResult edr = ap.ListDocuments((AccountId, "xxx-xx-xxx");
List<EnvelopeDocument> docs = edr.EnvelopeDocuments;
foreach(EnvelopeDocument doc in docs)
{
Stream stream1 = ap.GetDocument(AccountId, "xxx-xx-xxx", doc.DocumentId);
StreamReader reader = new System.IO.StreamReader(stream1, encode);
var data = reader.ReadToEnd();
StreamWriter writer = new StreamWriter(#"C:\mysigneddoc.pdf");
writer.Write(data);
writer.Close();
}
When I try to open the completed pdf, I get the error stating that
the signers identity has not been verified.
Any ideas where I might be going wrong?
Please look at the API recipe here to download the documents from an envelope.
var ap = new EnvelopesApi();
var edr = ap.ListDocuments((AccountId, "xxx-xx-xxx");
List<EnvelopeDocument> docs = edr.EnvelopeDocuments;
foreach(EnvelopeDocument doc in docs)
{
// GetDocument() API call returns a MemoryStream
var docStream = (MemoryStream)envelopesApi.GetDocument(accountId, envelopeId, doc.DocumentId);
// let's save the document to local file system
filePath = #"C:\temp\" + Path.GetRandomFileName() + ".pdf";
fs = new FileStream(filePath, FileMode.Create);
docStream.Seek(0, SeekOrigin.Begin);
docStream.CopyTo(fs);
fs.Close();
}
You can also download the combined documents in an envelope using the GetEnvelopeDocuments api. You are not required to query for each individual document.
Combined PDF
Pass the string combined as the documentId.
Retrieve a PDF that contains the combined content of all documents and the certificate.
string envelopeId = "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
string accountId = "XXXXXX";
var envApi = new EnvelopesApi();
// GetDocument() API call returns a MemoryStream
var docStream = (MemoryStream)envApi.GetDocument(accountId, envelopeId, "combined");
// let's save the document to local file system
string filePath = #"C:\temp\" + Path.GetRandomFileName() + ".pdf";
var fs = new FileStream(filePath, FileMode.Create);
docStream.Seek(0, SeekOrigin.Begin);
docStream.CopyTo(fs);
fs.Close();
ZIP file
Pass the string archive as documentId
Retrieve a ZIP archive that contains all of the PDF documents, the certificate, and any .WAV files used for voice authentication.
var envApi = new EnvelopesApi();
// GetDocument() API call returns a MemoryStream
var docStream = (FileStream)envApi.GetDocument(accountId, envelopeId, "archive");
// let's save the document to local file system
string filePath = #"C:\temp\" + Path.GetRandomFileName() + ".zip";
var fs = new FileStream(filePath, FileMode.Create);
docStream.Seek(0, SeekOrigin.Begin);
docStream.CopyTo(fs);
fs.Close();

Microsoft bot framework : How to define the image path which exists in the solution itself

I have stored the image inside the project itself, now I would like to display the image on the hero card, so I have mentioned the relative path. However the image is not appearing....
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: "~/duck-on-a-rock.jpg", alt:"image1"));
But when I referred the image from some website and mention the same path on the page like below that time the image is appearing.
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: "http://www.publicdomainpictures.net/pictures/30000/t2/duck-on-a-rock.jpg", alt:"image1"));
Is it not possible to keep the image inside the project folder?
This is a super old question, but google me brought here nonetheless. The solution I came across was to create a data url for your local resource. In node.js:
const imageData = fs.readFileSync(path.join(__dirname, '../resources/logo.png'));
const base64Image = Buffer.from(imageData).toString('base64');
const inlinePng = {
name: 'logo.png',
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64Image }`
};
With an svg, you can skip the base64 encode:
const svgData = fs.readFileSync(path.join(__dirname, './resources/logo.svg'));
const inlineSvg = {
name: 'logo',
contentType: 'image/svg',
contentUrl: `data:image/svg+xml;utf8,${ svgData }`,
};
See Microsoft's docs for reference and C# samples.
Using DI approach: Pass one extra IHostingEnvironment variable to your dialog constructor.
public DialogBot(ConversationState conversationState, T dialog, ConcurrentDictionary<string, ConversationReference> conversationReferences, IHostingEnvironment env)
{
_conversationReferences = conversationReferences;
_conversationState = conversationState;
_dialog = dialog;
_env = env;
}
And later you can use _env.WebRootPath to access your local storage (for bot emulator) or use environmental variable WEBSITE_HOSTNAME if you run bot in azure.
private async Task SendWelcomeCardAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var card = new HeroCard();
card.Title = "Welcome to Bot Framework!";
var host = new Uri(Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME") != null ? ("https://" + Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")) : _env.WebRootPath + "/");
var img = new Uri(host, "MyImage.jpg");
card.Images = new List<CardImage>() { new CardImage(img.AbsoluteUri) };
var response = MessageFactory.Attachment(card.ToAttachment());
await turnContext.SendActivityAsync(response, cancellationToken);
}
I simply dropped MyImage.jpg image to wwwroot project folder and marked it as Content

Resources