I have tried the code in a Console App. Following is the code:
Program.cs:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using TwitterWebhookDemo.Authentication;
namespace TwitterWebhookDemo
{
class Program
{
public static TweetyAuthContext AuthContext => new TweetyAuthContext()
{
AccessSecret = âAccessSecretâ,
AccessToken = âAccessTokenâ,
ConsumerKey = âConsumerKeyâ,
ConsumerSecret = âConsumerSecretâ
};
static void Main(string[] args)
{
Program p = new Program();
string url = âhttps://functionappmetersystem.azurewebsites.net/api/webhook/twitterâ;
p.CreateWebhook_1(AuthContext, url);
p.CreateWebhook_2(AuthContext, url);
p.GetRegisteredWebhooks(AuthContext);
}
public void CreateWebhook_1(TweetyAuthContext authContext, string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException(nameof(url));
}
//TODO: Provide a generic class to make Twitter API Requests.
string urlParam = Uri.EscapeUriString(url);
string env_name = "MeterSystemsDev";
string resourceUrl = $"https://api.twitter.com/1.1/account_activity/all/{env_name}/webhooks.json";
HttpResponseMessage response;
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(resourceUrl);
webRequest.Headers.Add("Authorization", AuthHeaderBuilder.Build(AuthContext, HttpMethod.Post, resourceUrl));
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
Stream stream = webRequest.GetRequestStream();
string postBody = "url=" + Uri.EscapeDataString(url);
byte[] bodyBytes = new ASCIIEncoding().GetBytes(postBody);
stream.Write(bodyBytes, 0, bodyBytes.Length);
stream.Flush();
stream.Close();
//Allow us a reasonable timeout in case Twitter's busy
webRequest.Timeout = 3 * 60 * 1000;
try
{
HttpWebResponse webResponse = webRequest.GetResponse() as HttpWebResponse;
Stream dataStream = webResponse.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
catch (Exception ex)
{
// throw ex;
}
}
public async void CreateWebhook_2(TweetyAuthContext authContext, string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException(nameof(url));
}
//TODO: Provide a generic class to make Twitter API Requests.
string urlParam = Uri.EscapeUriString(url);
string env_name = "MeterSystemsDev";
string resourceUrl = $"https://api.twitter.com/1.1/account_activity/all/{env_name}/webhooks.json?url={urlParam}";
string requestUrl = string.Format("https://api.twitter.com/1.1/account_activity/all/{0}/webhooks.json?url={1}", env_name, Uri.EscapeDataString(url));
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUrl);
webRequest.Headers.Add("Authorization", AuthHeaderBuilder.Build(AuthContext, HttpMethod.Post, resourceUrl));
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
//Allow us a reasonable timeout in case Twitter's busy
webRequest.Timeout = 3 * 60 * 1000;
try
{
HttpWebResponse webResponse = webRequest.GetResponse() as HttpWebResponse;
Stream dataStream = webResponse.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
catch (Exception ex)
{
// throw ex;
}
}
public async void GetRegisteredWebhooks(TweetyAuthContext authContext)
{
string env_name = "MeterSystemsDev";
string resourceUrl = $"https://api.twitter.com/1.1/account_activity/all/{env_name}/webhooks.json";
//string resourceUrl = "https://api.twitter.com/1.1/account/verify_credentials.json";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(resourceUrl);
webRequest.Headers.Add("Authorization", AuthHeaderBuilder.Build(AuthContext, HttpMethod.Get, resourceUrl));
webRequest.Method = "GET";
//Allow us a reasonable timeout in case Twitter's busy
webRequest.Timeout = 3 * 60 * 1000;
try
{
HttpWebResponse webResponse = webRequest.GetResponse() as HttpWebResponse;
Stream dataStream = webResponse.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
catch (Exception ex)
{
// throw ex;
}
}
}
}
AuthHeaderBuilder.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.WebUtilities;
namespace TwitterWebhookDemo.Authentication
{
/// <summary>
/// Twitter Authentication helper.
/// </summary>
public class AuthHeaderBuilder
{
/// <summary>
/// Returns a ready 'OAuth ..' prefixed header to set in any call to Twitter API.
/// </summary>
/// <param name="authContext">Twitter app auth context.</param>
/// <param name="method">The Request Http method.</param>
/// <param name="requestUrl">The Request uri along with any query parameter.</param>
/// <returns></returns>
public static string Build(TweetyAuthContext authContext, HttpMethod method, string requestUrl)
{
if (!Uri.TryCreate(requestUrl, UriKind.RelativeOrAbsolute, out Uri resourceUri))
{
throw new Exception("Invalid Resource Url format.");
}
if (authContext == null || !authContext.IsValid)
{
throw new Exception("Invalid Tweety Auth Context.");
}
string oauthVersion = "1.0";
string oauthSignatureMethod = "HMAC-SHA1";
// It could be any random string..
string oauthNonce = DateTime.Now.Ticks.ToString();
double epochTimeStamp = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
string oauthTimestamp = Convert.ToInt64(epochTimeStamp).ToString();
Dictionary<string, string> signatureParams = new Dictionary<string, string>();
signatureParams.Add("oauth_consumer_key", authContext.ConsumerKey);
signatureParams.Add("oauth_nonce", oauthNonce);
signatureParams.Add("oauth_signature_method", oauthSignatureMethod);
signatureParams.Add("oauth_timestamp", oauthTimestamp);
signatureParams.Add("oauth_token", authContext.AccessToken);
signatureParams.Add("oauth_version", oauthVersion);
var query = QueryHelpers.ParseQuery(resourceUri.Query);
List<KeyValuePair<string, string>> qParams = query.Select(qp => new KeyValuePair<string, string>(qp.Key, qp.Value)).ToList();
foreach (KeyValuePair<string, string> qp in qParams)
{
signatureParams.Add(qp.Key, qp.Value);
}
string baseString = string.Join("&", signatureParams.OrderBy(kpv => kpv.Key).Select(kpv => $"{kpv.Key}={kpv.Value}"));
string resourceUrl = requestUrl.Contains("?") ? requestUrl.Substring(0, requestUrl.IndexOf("?")) : requestUrl;
baseString = string.Concat(method.Method.ToUpper(), "&", Uri.EscapeDataString(resourceUrl), "&", Uri.EscapeDataString(baseString));
string oauthSignatureKey = string.Concat(Uri.EscapeDataString(authContext.ConsumerSecret), "&", Uri.EscapeDataString(authContext.AccessSecret));
string oauthSignature;
using (HMACSHA1 hasher = new HMACSHA1(Encoding.ASCII.GetBytes(oauthSignatureKey)))
{
oauthSignature = Convert.ToBase64String(hasher.ComputeHash(Encoding.ASCII.GetBytes(baseString)));
}
string headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " +
"oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " +
"oauth_token=\"{4}\", oauth_signature=\"{5}\", " +
"oauth_version=\"{6}\"";
string authHeader = string.Format(headerFormat,
Uri.EscapeDataString(oauthNonce),
Uri.EscapeDataString(oauthSignatureMethod),
Uri.EscapeDataString(oauthTimestamp),
Uri.EscapeDataString(authContext.ConsumerKey),
Uri.EscapeDataString(authContext.AccessToken),
Uri.EscapeDataString(oauthSignature),
Uri.EscapeDataString(oauthVersion));
return authHeader;
}
}
}
TweetyAuthContext.cs
namespace TwitterWebhookDemo.Authentication
{
/// <summary>
/// Required for any action, this represents the current user context.
/// </summary>
public class TweetyAuthContext
{
public string ConsumerKey { get; set; }
public string ConsumerSecret { get; set; }
public string AccessToken { get; set; }
public string AccessSecret { get; set; }
/// <summary>
/// Check if the current auth context is valid or not.
/// Null or Empty value for one on the auth properties will return false.
/// </summary>
public bool IsValid => !string.IsNullOrEmpty(ConsumerKey) && !string.IsNullOrEmpty(ConsumerSecret) && !string.IsNullOrEmpty(AccessToken) && !string.IsNullOrEmpty(AccessSecret);
}
}
In this sample GetWebhooks is returning Status OK but Create is always throwing âAuthorization Requiredâ.
Please check this and let me know what is wrong??