/// <summary> /// Post the message msg to the slack channel implied by the webhook. /// </summary> /// <param name="log">Logger instance.</param> /// <param name="msg"> Message to be posted.</param> /// <returns>Status code: 0 = success.</returns> public static async Task <int> PostSlackMessageAsync(ILogger log, string msg) { log.LogInformation("PostSlackMessageAsync: enter."); var slackWebHook = await KeyVaultAccessor.GetInstance().GetSecretAsync("AzTwitterSarSlackHook"); HttpClient httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(30); Uri mlFuncUri = new Uri(slackWebHook); /* Setting the property link_names is required for the channel * alert to work. Alternatively (not tried), see * https://discuss.newrelic.com/t/sending-alerts-to-slack-with-channel-notification/35921/3 */ var payload = JsonConvert.SerializeObject(new { text = $"{msg}", link_names = "1" }); var httpContent = new StringContent(payload, Encoding.UTF8, "application/json"); HttpResponseMessage httpResponseMsg = await httpClient.PostAsync(mlFuncUri, httpContent); if (httpResponseMsg.StatusCode == HttpStatusCode.OK && httpResponseMsg.Content != null) { var result = await httpResponseMsg.Content.ReadAsStringAsync(); log.LogInformation("PostSlackMessageAsync: response: " + result); } else { log.LogInformation($"PostSlackMessageAsync: posting to slack failed, response code: {httpResponseMsg.StatusCode}."); } log.LogInformation("PostSlackMessageAsync: exit."); return(0); }
public static async Task <MlResult> GetMlScore([ActivityTrigger] string tweet, ILogger log) { log.LogInformation("A_GetMlScore: Start."); string mlUriString = await KeyVaultAccessor.GetInstance().GetSecretAsync("AzTwitterSarAiUri"); MlResult result = new MlResult { Score = 0, Label = PublishLabel.NotAssigned, MlVersion = "" }; if (mlUriString is null) { log.LogError($"ML-inference link not configured."); } else { var payload = JsonConvert.SerializeObject(new { tweet = tweet }); var httpContent = new StringContent(payload, Encoding.UTF8, "application/json"); HttpClient httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(30); Uri mlFuncUri = new Uri(mlUriString); log.LogInformation($"Calling ML-inference."); HttpResponseMessage httpResponseMsg = null; try { httpResponseMsg = await httpClient.PostAsync(mlFuncUri, httpContent); } catch (Exception e) { log.LogError($"Getting ML score failed: {e.Message}"); } if (httpResponseMsg != null && httpResponseMsg.StatusCode == HttpStatusCode.OK && httpResponseMsg.Content != null) { var responseContent = await httpResponseMsg.Content.ReadAsStringAsync(); ResponseData ml_result = JsonConvert.DeserializeObject <ResponseData>(responseContent); result.Score = ml_result.Score; result.Label = ml_result.Label == 1 ? PublishLabel.Positive : PublishLabel.Negative; result.MlVersion = ml_result.Version; } } log.LogInformation("A_GetMlScore: Done."); return(result); }
public static KeyVaultAccessor GetInstance() { // C# thread-safe singleton pattern from refactoring.guru // This conditional is needed to prevent threads stumbling over the // lock once the instance is ready. if (_instance == null) { lock (_lock) { // The first thread to acquire the lock, reaches this // conditional, goes inside and creates the Singleton // instance. Once it leaves the lock block, a thread that // might have been waiting for the lock release may then // enter this section. But since the Singleton field is // already initialized, the thread won't create a new // object. if (_instance == null) { _instance = new KeyVaultAccessor(); } } } return(_instance); }
public static async Task <List <TweetProcessingData> > GetTweets([ActivityTrigger] string lastTweetId, ILogger log) { log.LogInformation($"A_GetTweets: Getting new tweets after {lastTweetId}."); KeyVaultAccessor kva = KeyVaultAccessor.GetInstance(); string apiKey = await kva.GetSecretAsync("TwitterApiKey"); // aka consumer key string apiSecretKey = await kva.GetSecretAsync("TwitterApiSecretKey"); // aka consumer secret string accessToken = await kva.GetSecretAsync("TwitterAccessToken"); string accessTokenSecret = await kva.GetSecretAsync("TwitterAccessTokenSecret"); string monitoredTwitterAccount = Environment.GetEnvironmentVariable("MonitoredTwitterAccount"); List <TweetProcessingData> tpds = new List <TweetProcessingData>(); // return value, empty list if Twitter connection failure var userCredentials = Auth.SetUserCredentials(apiKey, apiSecretKey, accessToken, accessTokenSecret); try { // This variable is not used later, but seems to leave an id for the library. var authenticatedUser = User.GetAuthenticatedUser(userCredentials); } catch (AggregateException ae) { // Inserted try-catch after a seemingly intermittent exception that lead to // the service stopping completely, 20210102, ca. 11 am. log.LogWarning($"A_GetTweets: User authentication failure: {ae.Message}. Return no tweets and retry in next cycle."); return(tpds); } // Note: The following does NOT get MaximumNumberOfResults tweets // from after lastTweetId!!! Rather it gets the most recent // tweets with the early limit defined by lastTweetId OR the // defined maximum, whichever is more limiting! // (Therefore, in order to test on past tweets, one may need // to increase MaximumNumberOfResults considerably to get ALL // tweets from the one targeted to the current one. SearchTweetsParameters searchParameter = new SearchTweetsParameters($"from:{monitoredTwitterAccount}") { MaximumNumberOfResults = 15, SinceId = long.Parse(lastTweetId) }; IEnumerable <ITweet> tweets = null; try { tweets = await SearchAsync.SearchTweets(searchParameter); } catch (Exception e) { // Inserted try-catch after a seemingly intermittent exception that lead to // the service stopping completely, 20201213, ca. 7 am. log.LogWarning($"A_GetTweets: SearchTweets failed with exception: {e.Message}"); } if (tweets is null) { log.LogWarning($"A_GetTweets: Twitter connection failure. Return no tweets and retry in next cycle."); return(tpds); } // Since the further processing can scramble the order again, we don't need to sort here. foreach (var tweet in tweets) { // Copy the data that we need over to a serializable struct. TweetProcessingData tpd = new TweetProcessingData(); tpd.IdStr = tweet.IdStr; tpd.CreatedAt = tweet.CreatedAt; tpd.FullText = tweet.FullText; tpd.Hashtags = String.Join("|", tweet.Hashtags.Select(t => t.Text)); tpd.InReplyToStatusIdStr = tweet.InReplyToStatusIdStr; tpd.Url = tweet.Url; tpd.TextWithoutTags = TweetAnalysis.removeHashtagsFromText(tweet.FullText, tweet.Hashtags); tpds.Add(tpd); } log.LogInformation($"A_GetTweets: Done, got {tweets.Count()} new tweets."); return(tpds); }