I have successfully called Facebook Conversion API in C# using hardcoded sample data. Code below:
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",<MyToken>);
Int64 unitTimeStamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var fbData= new
{
data = new[] {
new {
event_name="Purchase",
event_time = 1616695650,
action_source= "email",
user_data = new
{
em= "7b17fb0bd173f625b58636fb796407c22b3d16fc78302d79f0fd30c2fc2fc068",
ph= ""
},
custom_data = new
{
currency= "USD",
value= "142.52"
}
}
}
};
var postResponse = await httpClient.PostAsJsonAsync("https://graph.facebook.com/v10.0/<MyPixelID/events", fbData);
postResponse.EnsureSuccessStatusCode();
However when using 'real' data, Facebook requires some types to be hashed using SHA-256 (e.g. email):
https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters#normalize-and-hash
I cannot work out how to do this in ASP.NET. I've tried using the below function but this didn't seem to work:
public static string GenerateSHA256(string input)
{
var bytes = Encoding.Unicode.GetBytes(input);
using (var hashEngine = SHA256.Create())
{
var hashedBytes = hashEngine.ComputeHash(bytes, 0, bytes.Length);
var sb = new StringBuilder();
foreach (var b in hashedBytes)
{
var hex = b.ToString("x2");
sb.Append(hex);
}
return sb.ToString();
}
}
On a side note, how would Facebook read the data if I did hash it using SHA-256 as I understood this to be irreversible?
Issue is with this line
var bytes = Encoding.Unicode.GetBytes(input);
which is UTF16. You should use UTF8.
var bytes = Encoding.UTF8.GetBytes(input);
With regards to your side question, here is what facebook says
https://developers.facebook.com/docs/marketing-api/audiences/guides/custom-audiences#hash
As the owner of your business's data, you are responsible for creating
and managing this data. This includes information from your Customer
Relationship Management (CRM) systems. To create audiences, you must
share your data in a hashed format to maintain privacy. See Hashing
and Normalizing Data. Facebook compares this with our hashed data to
see if we should add someone on Facebook to your ad's audience.
Related
I want to do send a request to api method. But I need to verify signature. I'm using openssl. Firstly I'm creating a signature in this method;
var certificate = new X509Certificate2(certificatePath, certificatePass, X509KeyStorageFlags.Exportable);
var originalData = Encoding.UTF8.GetBytes(dataToSign);
using (var rsa = certificate.GetRSAPrivateKey())
{
var signeddata = rsa.SignData(originalData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signeddata);
}
Then need to verify with this code;
var certificate = new X509Certificate2(ceftificatePath, "certificatePass", X509KeyStorageFlags.MachineKeySet);
var dataToVerifyBytes = Encoding.UTF8.GetBytes(dataToVerify);
var signatureBytes = Convert.FromBase64String(signature);
using (var rsaAlg = (RSACryptoServiceProvider)certificate.PublicKey.Key)
using (var sha256 = new SHA256Managed())
{
return rsaAlg.VerifyData(dataToVerifyBytes, sha256, signatureBytes);
}
But I'm getting error when verified.
Verified Error
Error detail is;
Unable to cast object of type 'System.Security.Cryptography.RSACng' to type 'System.Security.Cryptography.RSACryptoServiceProvider'.
Also I'm trying this with .NET framework 4.8.0.
Take a look at this post on a separate website. It looks like your exactly question. Try to look up your question before posting it on stackoverflow next time.
I'm creating a C# application that uses DialogFlow's detectIntent. I need help passing the Google Cloud credentials explicitly.
It works with the GOOGLE_APPLICATION_CREDENTIALS environment variable. However I want to pass the credentials explicitly. I need a C# version of the solution provided here.
I'm using the following quick-start provided with the documentation:
public static void DetectIntentFromTexts(string projectId,
string sessionId,
string[] texts,
string languageCode = "en-US")
{
var client = df.SessionsClient.Create();
foreach (var text in texts)
{
var response = client.DetectIntent(
session: new df.SessionName(projectId, sessionId),
queryInput: new df.QueryInput()
{
Text = new df.TextInput()
{
Text = text,
LanguageCode = languageCode
}
}
);
var queryResult = response.QueryResult;
Console.WriteLine($"Query text: {queryResult.QueryText}");
if (queryResult.Intent != null)
{
Console.WriteLine($"Intent detected: {queryResult.Intent.DisplayName}");
}
Console.WriteLine($"Intent confidence: {queryResult.IntentDetectionConfidence}");
Console.WriteLine($"Fulfillment text: {queryResult.FulfillmentText}");
Console.WriteLine();
}
}
Currently you need to create a gRPC channel directly, and pass that into the client:
GoogleCredential credential = GoogleCredential.FromFile("...");
ChannelCredentials channelCredentials = credential.ToChannelCredentials();
Channel channel = new Channel(SessionsClient.DefaultEndpoint, channelCredentials);
var client = df.SessionsClient.Create(channel);
Very soon, this will be a lot easier via a builder pattern:
var client = new SessionsClientBuilder
{
CredentialsPath = "path to file",
}.Build();
... or various other ways of specify the credential. I'm hoping that'll be out in the next couple of weeks.
I am porting a gRPC client from python to c#. Both the python client and the c# client are using the gRPC Framework from grpc.io.
The python client uses the following code to open a secure, non-authenticated channel, which it then uses to procure a token string, which it then uses to create call credentials with the grpc.composite_channel_credentials() function:
channel = grpc.secure_channel(url_server_address, ssl_creds)
stub = gateway.GatewayStub(channel)
# Acquire access token via password authentication
pw_cmd = gateway.PasswordAuthenticateCmd(account_name=url.username, password=url.password)
auth_rsp = stub.PasswordAuthenticate(pw_cmd)
# Open a secure, authenticated channel
auth_creds = grpc.access_token_call_credentials(auth_rsp.access_token)
composite_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds)
channel = grpc.secure_channel(url_server_address, composite_creds)
stub = gateway.GatewayStub(channel)
In c#, I have been able to compile the protocol buffer definitions, and connect with the generated client to successfully acquire the access token:
SslCredentials secureChannel = new SslCredentials(File.ReadAllText(SSLCertificatePath));
Channel channel = new Channel(ServerURL, PortNum, secureChannel);
var client = new GrpcClient(new Grpc.Gateway.GatewayClient(channel));
var response = client.client.PasswordAuthenticate(new PasswordAuthenticateCmd() { AccountName = UserName, Password = UserPassword });
Console.WriteLine(response.AccessToken);
From here, however, I can't find the c# analog to the grpc.composite_channel_credentials() function to take the SslCredentials and the access token string to create combined credentials.
None of the examples here https://grpc.io/docs/guides/auth.html here use a token string, and I haven't been able to find any other examples out there.
What you're looking for is:
https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Core/CallCredentials.cs#L49 and https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Core/ChannelCredentials.cs#L67.
Feel free to also look at:
https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs#L58
I solved my problem using CallCredentials.FromInterceptor().
The grpc.access_token_call_credentials() python call adds an authorization entry to the metadata, and sets its value to "Bearer " + AccessToken, so I just had to do the same:
SslCredentials secureCredentials = new SslCredentials(File.ReadAllText(SSLCertificatePath));
Channel secureChannel = new Channel(ServerURL, PortNum, secureCredentials);
var client = new GrpcClient(new Grpc.Gateway.GatewayClient(secureChannel));
var response = client.client.PasswordAuthenticate(new PasswordAuthenticateCmd() { AccountName = UserName, Password = UserPassword });
var accessTokenCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) =>
{
metadata.Add("authorization", "Bearer " + passwordResponse.AccessToken);
return TaskUtils.CompletedTask;
}));
var authenticatedCredentials = ChannelCredentials.Create(secureCredentials, accessTokenCredentials);
Channel authenticatedChannel = new Channel(hostURL, hostPort, authenticatedCredentials);
As Jan pointed out in his answer, there is a function in the Grpc.Auth namespace that does the same thing as the function that I wrote: https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs#L58
Since twitter is depreciating API version 1 soon I've decided to convert an older Application to allow it to work with the new 1.1 API. From what I know about 1.1 I know you have to authenticate before making a call and use JSON rather than RSS for serializing the data. The application is WPF coded using xmal and c#
I am able to successfully authenticate using the LINQ to Twitter Library but I am lost when it comes to using JSON. Here is my code that I used for API v1
else if (auth.IsAuthorized && i == 2)
{
SyndicationClient client = new SyndicationClient();
SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=ScreenName"));
{
_model.Tweets.Clear();
foreach (var item in feed.Items)
{
_model.Tweets.Add(new Tweet
{
Name = "#ExampleHandle",
Message = item.Title.Text,
Image = new BitmapImage(new Uri("ms-appx:Assets/test_image", UriKind.RelativeOrAbsolute)),
});
}
}
}
}
And here is the code for the tweet class
public class Tweet
{
public String Name { get; set; }
public String Message { get; set; }
public ImageSource Image { get; set; }
}
I was wondering if someone could point me in the right direction for writing the JSON equivalent of this. Thanks in advance!
For those you read this question later on I was able to solve this problem. Below are my answers depending on your situation.
If you simply want to use Json instead of RSS you can do it like this:
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.json?screen_name=ScreenName"));
string ApiResponse = await response.Content.ReadAsStringAsync();
List<Tweet> tweets = await JsonConvert.DeserializeObjectAsync<List<Tweet>>(ApiResponse);
_model.Tweets.Clear();
foreach (var item in tweets)
{
_model.Tweets.Add(new Tweet
{
Name = "#UserName",
Message = item.Text,
Image = new BitmapImage(new Uri("ms-appx:Assets/sampleLocalImage", UriKind.RelativeOrAbsolute)),
});
However because of API 1.1 you must be authenticated before EACH call to the API for this is used Linq to Twitter. Here is the code for Authorization:
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = TwitterSettings.ConsumerKey,
ConsumerSecret = TwitterSettings.ConsumerKeySecret,
OAuthToken = TwitterSettings.AccessToken,
AccessToken = TwitterSettings.AccessTokenSecret,
}
};
auth.Authorize();
And the Code to Perform a Search(This is the code you want to use if using Twitter API 1.1):
var twitterCtx = new TwitterContext(auth);
var statusTweets =
from tweet in twitterCtx.Status
where tweet.Type == StatusType.User
&& tweet.ScreenName == "ScreenName"
select tweet;
_model.Tweets.Clear();
foreach (var item in statusTweets)
{
_model.Tweets.Add(new Tweet
{
Name = item.User.Name,
Message = item.Text,
Image = new BitmapImage(new Uri(item.User.ProfileImageUrl)),
});
I'm not familiar with the Twitter API, but I would assume some combination of HttpClient (if you're on .NET 4.0, you can get it here) and Newtonsoft.Json would be appropriate.
Newtonsoft.Json is not authored by Microsoft, but it is the package that everyone uses (including Microsoft's default web templates). The old Microsoft JSON serialization stuff is pretty much dead at this point.
I'm trying to update a user's Twitter status from my C# application.
I searched the web and found several possibilities, but I'm a bit confused by the recent (?) change in Twitter's authentication process. I also found what seems to be a relevant StackOverflow post, but it simply does not answer my question because it's ultra-specific regading a code snippet that does not work.
I'm attempting to reach the REST API and not the Search API, which means I should live up to the stricter OAuth authentication.
I looked at two solutions. The Twitterizer Framework worked fine, but it's an external DLL and I would rather use source code. Just as an example, the code using it is very clear and looks like so:
Twitter twitter = new Twitter("username", "password");
twitter.Status.Update("Hello World!");
I also examined Yedda's Twitter library, but this one failed on what I believe to be the authentication process, when trying basically the same code as above (Yedda expects the username and password in the status update itself but everything else is supposed to be the same).
Since I could not find a clear cut answer on the web, I'm bringing it to StackOverflow.
What's the simplest way to get a Twitter status update working in a C# application, without external DLL dependency?
Thanks
If you like the Twitterizer Framework but just don't like not having the source, why not download the source? (Or browse it if you just want to see what it's doing...)
I'm not a fan of re-inventing the wheel, especially when it comes to products that already exist that provide 100% of the sought functionality. I actually have the source code for Twitterizer running side by side my ASP.NET MVC application just so that I could make any necessary changes...
If you really don't want the DLL reference to exist, here is an example on how to code the updates in C#. Check this out from dreamincode.
/*
* A function to post an update to Twitter programmatically
* Author: Danny Battison
* Contact: gabehabe#hotmail.com
*/
/// <summary>
/// Post an update to a Twitter acount
/// </summary>
/// <param name="username">The username of the account</param>
/// <param name="password">The password of the account</param>
/// <param name="tweet">The status to post</param>
public static void PostTweet(string username, string password, string tweet)
{
try {
// encode the username/password
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
// determine what we want to upload as a status
byte[] bytes = System.Text.Encoding.ASCII.GetBytes("status=" + tweet);
// connect with the update page
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
// set the method to POST
request.Method="POST";
request.ServicePoint.Expect100Continue = false; // thanks to argodev for this recent change!
// set the authorisation levels
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType="application/x-www-form-urlencoded";
// set the length of the content
request.ContentLength = bytes.Length;
// set up the stream
Stream reqStream = request.GetRequestStream();
// write to the stream
reqStream.Write(bytes, 0, bytes.Length);
// close the stream
reqStream.Close();
} catch (Exception ex) {/* DO NOTHING */}
}
Another Twitter library I have used sucessfully is TweetSharp, which provides a fluent API.
The source code is available at Google code. Why don't you want to use a dll? That is by far the easiest way to include a library in a project.
The simplest way to post stuff to twitter is to use basic authentication , which isn't very strong.
static void PostTweet(string username, string password, string tweet)
{
// Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication
WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = username, Password = password } };
// Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body
ServicePointManager.Expect100Continue = false;
// Construct the message body
byte[] messageBody = Encoding.ASCII.GetBytes("status=" + tweet);
// Send the HTTP headers and message body (a.k.a. Post the data)
client.UploadData("http://twitter.com/statuses/update.xml", messageBody);
}
Try LINQ To Twitter. Find LINQ To Twitter update status with media complete code example that works with Twitter REST API V1.1. Solution is also available for download.
LINQ To Twitter Code Sample
var twitterCtx = new TwitterContext(auth);
string status = "Testing TweetWithMedia #Linq2Twitter " +
DateTime.Now.ToString(CultureInfo.InvariantCulture);
const bool PossiblySensitive = false;
const decimal Latitude = StatusExtensions.NoCoordinate;
const decimal Longitude = StatusExtensions.NoCoordinate;
const bool DisplayCoordinates = false;
string ReplaceThisWithYourImageLocation = Server.MapPath("~/test.jpg");
var mediaItems =
new List<media>
{
new Media
{
Data = Utilities.GetFileBytes(ReplaceThisWithYourImageLocation),
FileName = "test.jpg",
ContentType = MediaContentType.Jpeg
}
};
Status tweet = twitterCtx.TweetWithMedia(
status, PossiblySensitive, Latitude, Longitude,
null, DisplayCoordinates, mediaItems, null);
Try TweetSharp . Find TweetSharp update status with media complete code example works with Twitter REST API V1.1. Solution is also available for download.
TweetSharp Code Sample
//if you want status update only uncomment the below line of code instead
//var result = tService.SendTweet(new SendTweetOptions { Status = Guid.NewGuid().ToString() });
Bitmap img = new Bitmap(Server.MapPath("~/test.jpg"));
if (img != null)
{
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
Dictionary<string, Stream> images = new Dictionary<string, Stream>{{"mypicture", ms}};
//Twitter compares status contents and rejects dublicated status messages.
//Therefore in order to create a unique message dynamically, a generic guid has been used
var result = tService.SendTweetWithMedia(new SendTweetWithMediaOptions { Status = Guid.NewGuid().ToString(), Images = images });
if (result != null && result.Id > 0)
{
Response.Redirect("https://twitter.com");
}
else
{
Response.Write("fails to update status");
}
}
Here's another solution with minimal code using the excellent AsyncOAuth Nuget package and Microsoft's HttpClient. This solution also assumes you're posting on your own behalf so you have your access token key/secret already, however even if you don't the flow is pretty easy (see AsyncOauth docs).
using System.Threading.Tasks;
using AsyncOAuth;
using System.Net.Http;
using System.Security.Cryptography;
public class TwitterClient
{
private readonly HttpClient _httpClient;
public TwitterClient()
{
// See AsyncOAuth docs (differs for WinRT)
OAuthUtility.ComputeHash = (key, buffer) =>
{
using (var hmac = new HMACSHA1(key))
{
return hmac.ComputeHash(buffer);
}
};
// Best to store secrets outside app (Azure Portal/etc.)
_httpClient = OAuthUtility.CreateOAuthClient(
AppSettings.TwitterAppId, AppSettings.TwitterAppSecret,
new AccessToken(AppSettings.TwitterAccessTokenKey, AppSettings.TwitterAccessTokenSecret));
}
public async Task UpdateStatus(string status)
{
try
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{"status", status}
});
var response = await _httpClient.PostAsync("https://api.twitter.com/1.1/statuses/update.json", content);
if (response.IsSuccessStatusCode)
{
// OK
}
else
{
// Not OK
}
}
catch (Exception ex)
{
// Log ex
}
}
}
This works on all platforms due to HttpClient's nature. I use this method myself on Windows Phone 7/8 for a completely different service.