How to download MMS Media from Twilio using the .NET SDK - c#

I have a project that my team is working on to send and receive Twilio MMS Messages. We have used the Twilio Client in the C# SDK for years to successfully send messages. Now with this project, it is clear how to determine there are Media files sent with the MMS Message and locate them, but we need some help downloading them.
I am aware that we can write the code in C# and add the necessary authentication, but I was hoping to keep all our activities within the Twilio SDK and client so the code is easily accessible to pick up by any of our current or potential future developers. Is there a way to download a file through the Twilio client? I didn't see any examples in the docs.

The Twilio SDK can help you locate the media of MMS messages, and information about the media is also passed into your webhook handlers. However, the SDK doesn't have any classes/methods to help you download the media files.
By default, the file is publicly accessible if you know the URL, and doesn't require authentication. You can enable basic auth for your MMS media files so only you can download them by authenticating with your Account SID and Auth Token or API Key SID and API Key Secret.
Here's a C# sample on how to do download the file with and without basic auth:
using System.Net.Http.Headers;
using System.Text;
const string accountSid = "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const string messageSid = "MMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const string mediaSid = "MExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var mediaUrl = $"https://api.twilio.com/2010-04-01/Accounts/{accountSid}/Messages/{messageSid}/Media/{mediaSid}";
const string localFilePath = "image.jpg";
using var httpClient = new HttpClient();
using var httpRequest = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(mediaUrl)
};
const bool useBasicAuth = true;
if (useBasicAuth)
{
var authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
var authHeaderValue = $"{accountSid}:{authToken}";
authHeaderValue = Convert.ToBase64String(Encoding.ASCII.GetBytes(authHeaderValue));
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
}
using var response = await httpClient.SendAsync(httpRequest);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"HTTP Status Code: {response.StatusCode}");
Console.WriteLine($"Response body: {await response.Content.ReadAsStringAsync()}");
return;
}
await using var fileStream = new FileStream(localFilePath, FileMode.CreateNew);
await response.Content.CopyToAsync(fileStream);

Related

Get instagram full profile information without "Instagram Graph Api"

I wanna get some data from the Instagram users.
So I've used Instagram Basic Display Api and the profile data I could receive was these:
username
media count
account type
but I want these data:
username
name
media count
Profile Image
followers count
following count
I don't know how can I have these data without Instagram Graph API(in any way) in c#?
Or is there any way to get these data with the WebClient class or anything like that?
Update for #Eehab answer: I use RestClient and WebClient in this example and both of them give the same result.
Now see WebClient example:
WebClient client = new WebClient();
string page = client.DownloadString("https://www.instagram.com/instagram/?__a=1");
Console.WriteLine(page);
Console.ReadKey();
and see an image of this code here.
now see the result of the code above here
I've also got, that this link is the only access for login users and I've been login into my Instagram account in chrome already, but I think WebClient needs to log in too.
Edit Through #Eehab answer:
In this case for using this Url(https://www.instagram.com/{username}/?__a=1), we can't do it without Instagram logged-in browser profile. So we should log in to Instagram with selenium and use the logged-in cookies to use it for Url requests. So first Install the selenium web driver and then write the following codes(untested):
var driver = new ChromeDriver();
//go to Instagram
driver.Url = "https://www.instagram.com/";
//Log in
var userNameElement = _driver.FindElement(By.Name("username"));
userNameElement.SendKeys("Username");
var passwordElement = _driver.FindElement(By.Name("password"));
passwordElement.SendKeys(Cars[0].auth.pass);
var loginButton = _driver.FindElement(By.Id("login"));
loginButton.Click();
//Get cookies
var cookies = driver.Manage().Cookies.AllCookies.ToList();
//Send request with given cookies :)
var url = "https://www.instagram.com/{username}/?__a=1";
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
foreach(var cookie in cookies){
httpRequest.Headers["Cookie"] += $"{cookie.Name}={cookie.Value}; ";
}
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
//...
If anyone can improve this question for more uses can edit and I really appreciate it :)
You could do that using the open API , example :
https://www.instagram.com/instagram/?__a=1
example code from postman code :
var client = new RestClient("https://www.instagram.com/instagram/?__a=1");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
you could use HttpClient class also, if you want to use WebClient you could do it with
WebClient.DownloadString Method while I don't recommend using WebClient for this scraping, keep in mind Instagram may block you if blocked you , you need residential proxies to bypass the block.
the response will be json data , use Json.Net or similar library to deserialize it.
just replace instagram with any username you want in the given url.

Passing current authentication credentials from UWP app to web service to access resources on server with these credentials

My Windows 10 UWP app is calling a WebAPI web service that I have created. I need to pass the current credentials on the client side when calling the web service so that it can access other resources using these credentials.
I also need to do this without prompting the user for credentials so that the experience is seamless.
I am able to do this with using System.Net.Http and successfully pass the current credentials to the server to use for accessing resources. This sends the request and brings back the response without any prompt. I have enabled Enterprise Authentication and Private Networks capabilities on the UWP app to make this work.
Problem: This works fine for GET requests but not for POST requests to the same server. POST requests result in the following error:
This IRandomAccessStream does not support the GetInputStreamAt method
because it requires cloning and this stream does not support cloning.
I read that this was a bug on this link: PostAsync throwing IRandomAccessStream error when targeting windows 10 UWP. The workaround proposed in multiple locations for this bug is to use Windows.Web.Http instead. However, if I do this, how can I pass the default/current credentials to the server?
Here is the code that I am using to do a GET request using the current Windows credentials without prompting for it. It works flawlessly:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Method = System.Net.Http.HttpMethod.Get,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//This also works fine
using (System.Net.Http.HttpResponseMessage response = await client.GetAsync(strWebServiceURL))
{
responseContent = await response.Content.ReadAsStringAsync();
}
Below is the code I use to do a POST request which results in the IRandomAccessStream error:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Content = myMultipartFormDataContent,
Method = System.Net.Http.HttpMethod.Post,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//No difference when using it this way as well
using (System.Net.Http.HttpResponseMessage response = await client.PostAsync(strWebServiceURL, myMultipartFormDataContent))
{
responseContent = await response.Content.ReadAsStringAsync();
}
I tried using Windows.Web.Http but I don't know how I can get it to pass the current/default credentials to the server without prompting.
I have also added the WebService URL to a IE Local Intranet zone and have that zone set to automatically log in with current user name and password:
Please help!
With the new Windows.Web.Http namespace in UWP app, if you want to use the DefaultCredentials, all you have to do is turn on enterprise credentials in the manifest and the uwp app will send them out as appropriate. You don't need to configure anything on the HttpClientto make it work. Details please reference this thread.
Since you already enable the enterprise credentials capability, you could just create HttpClient without configure. But to avoid the username and password prompt, you may need to disable the UI, for example:
var myFilter = new HttpBaseProtocolFilter();
myFilter.AllowUI = false;
Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(myFilter);
Windows.Web.Http.HttpResponseMessage result = await client.GetAsync(new Uri("http://localhost:5132/api/values"));

Streaming Content constantly updating 8x8 streaming service

I have tried to create a simple console application.
We have a call system from 8x8 that provide a web streaming API but their documentation is very limited and nothing in C#.
The api service streams call statuses in near real time and I would like to get that 'stream' and be able to read and process it in realtime if possible. The response or Content Type is 'text/html'. But the actual body of the response can be declared as json - sample below:
{"Interaction":{"attachedData":{"attachedDatum":[{"attachedDataKey":"#pri","attachedDataValue":100},{"attachedDataKey":"callingName","attachedDataValue":999999999999},{"attachedDataKey":"cha","attachedDataValue":99999999999},{"attachedDataKey":"cnt","attachedDataValue":0},{"attachedDataKey":"con","attachedDataValue":0},{"attachedDataKey":"med","attachedDataValue":"T"},{"attachedDataKey":"pho","attachedDataValue":9999999999},{"attachedDataKey":"phoneNum","attachedDataValue":9999999999},{"attachedDataKey":"tok","attachedDataValue":999999999}]},"event":"InteractionCreated","inboundChannelid":9999999999,"interactionEventTS":9999999,"interactionGUID":"int-15b875d0da2-DJOJkDhDsrh3AIaFP8VkICv9t-phone-01-testist","resourceType":0}}
I have seen several posts concerning httpClient and the GetAsync methods but none of these appear to work as they appear to be for calls when a response is made, not something that constantly has a response.
Using fiddler for the call it does not appear to close so the stream is constantly running, so fiddler does not display any data until a separate user or instance connects.
When I use a browser the content is 'streamed' to the page and updates automatically and shows all the content (as above).
The api contains authentication so when another client connects and retrieves data the connected client closes and finally I am able to see the data that was gathering.
This is the code so and does return the big stream when another client connects but ideally I want a real time response and appears to just get stuck in the GETASYNC method:
var response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = await responseContent.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Hopefully that's enough information for one of you clever people to help me in my predicament.
I was also having an issue consuming their streaming API and the examples I found that worked with the Twitter and CouchBase streaming API's did not work with 8x8. Both Twitter and CouchBase send line terminators in their pushes so the solution relied on ReadLine to pull in the feed. Since 8x8 does not send terminators you'll need to use ReadBlock or better ReadBlockAsync.
The following code shows how to connect using credentials and consume their feed:
private static async Task StreamAsync(string url, string username, string password)
{
var handler = new HttpClientHandler()
{
Credentials = new NetworkCredential {UserName = username, Password = password},
PreAuthenticate = true
};
// Client can also be singleton
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Connection.Add("keep-alive");
using (var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead))
{
using (var body = await response.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(body))
{
while (!reader.EndOfStream)
{
var buffer = new char[1024];
await reader.ReadBlockAsync(buffer, 0, buffer.Length);
Console.WriteLine(new string(buffer));
}
}
}
}
}
}

Unable to download the content of the folder via shared link using Microsoft.Graph

I am trying to fetch the file contained in the shared folder in Onedrive using Microsoft.Graph API. My code is:
public static GraphServiceClient GetGraphServiceClient()
{
if (graphClient == null)
{
// Create Microsoft Graph client.
try
{
graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var token = await GetTokenForUserAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}));
return graphClient;
}
catch (Exception ex)
{
}
}
void GetFile()
{
graphclient = AuthenticationHelper.GetGraphServiceClient();
var request = graphclient.Shares[get_encoded_link()].Root.Children;
var foundFile = await request.Request().GetAsync();
var request1 = graphclient.Shares[get_encoded_link()].Items[foundFile[0].Id].Content;
var content = await request1.Request().GetAsync();
}
In foundfile i get the all the details of the file that are in folder. But when i request the content of the file using its id then i get this error
"Unexpected exception returned from the service"
I also try to request the content using root.itemsPath[filename]
var request1 = graphclient.Shares[get_encoded_link()].Root.ItemsPath[filename];
Shared link is created by other user with the Scope type of "anonymous" and Link type is "edit" and the user who has signed in my app is try to accessing the content of the shared folder.
But get the same error.
I can't find what i am doing wrong here!
Thanks for your feedback. There seems to be an issue in Microsoft Graph API when downloading content form a sharing link created by other user with the "anonymous" scope.
To see what happens here, you can take advantage of fiddler and also the source code of Microsoft Graph .NET Client Library. You can download the source code form Releases and add them into your project to debug.
When you call var content = await request1.Request().GetAsync();, it will eventually call SendAsync method to send a request like the following:
As the document said, this returns a 302 Found response redirecting to a pre-authenticated download URL for the file. And the download URL is in the Location header in the response.
Graph Library handles redirect with HandleRedirect method and sends another request with the download URL in Location. However, this request returns a 403 Forbidden response.
For 403 Forbidden response, Graph Library throws UnexpectedExceptionResponse. That's the reason why you got "Unexpected exception returned from the service." error.
Workaround:
We can't download the content from download URL returned by Microsoft Graph API, but we can download while using browser. So I inspected the HTTP(s) traffic while downloading with Edge and found the API endpoint used in browser is different.
As you can see it used api.onedrive.com as the endpoint not graph.microsoft.com. So we can change the RequestUri as a workaround.
var sharingLink = #"{Sharing Link}";
var foundFile = await graphClient.Shares[UrlToSharingToken(sharingLink)].Root.Children.Request().GetAsync();
var request = graphClient.Shares[UrlToSharingToken(sharingLink)].Items[foundFile[0].Id].Content.Request().GetHttpRequestMessage();
request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("graph.microsoft.com", "api.onedrive.com"));
var response = await graphClient.HttpProvider.SendAsync(request);
var stream = await response.Content.ReadAsStreamAsync();

Insert photo ERROR: The remote server returned an error: (403) Forbidden

I need your help!.
Im trying to insert a new photo into a Picasa Album using Oauth 2.0 and a simple HttpRequest process. The result is that I cant insert a new photo into my Picasa web album after following the instructions listed on: https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol#Auth
I also have to say that I tried using the .Net library that they provide with the same results.
The implementation that I'm using now is the following:
public static string PostImage(
string streamImageConvertedToString)
{
string url = string.Format("https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}", "username#gmail.com", "idAlbum");
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "image/jpeg";
request.ContentLength = Encoding.UTF8.GetByteCount(data);
request.Method = "POST";
request.Headers.Add("GData-Version", "2");
request.Headers.Add("Slug", "cute_baby_kitten.jpg");
request.Headers.Add("Authorization", "Bearer " + GetToken());
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();
}
return result;
}
private static string GetToken() {
const string ServiceAccountEmail = "someid#developer.gserviceaccount.com";
var servicio = new PicasaService(null);
var certificate = new X509Certificate2(HttpContext.Current.Server.MapPath("/key2.p12"), "notasecret", X509KeyStorageFlags.Exportable);
var serviceAccountCredentialInitializer =
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://picasaweb.google.com/data/" }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
throw new InvalidOperationException("Access token request failed.");
return credential.Token.AccessToken;
}
Any help is welcome!!
(403) Forbidden
Means that you are trying to use a method insert which requires authorization to do.
you are connecting to service account someid#developer.gserviceaccount.com which should give you access to someid#developer.gserviceaccount.com pictures then.
you appear to be trying to access username#gmail.com unless you have given someid#developer.gserviceaccount.com access to insert pictures on behalf of username#gmail.com (Which I am not even sure is possible) you are not going to have permission to do this.
Remember a service account is a sudo user it has its own drive account, calendar account ... it does not have access to a random users data unless that user has given them access like they would any other user.
Note: Google .net client library does not support gdata APIs. Picasa is a gdata library I like how are trying to merge the two I am have to test this.
You're best (imho) approach would be to forget libraries and forget service accounts. Get a refresh token for the google user account you're trying to insert to, and use the raw HTTP REST API to invoke Picasa.

Categories

Resources