public static Dictionary <string, GithubActor> GetDistinct(GithubApi githubApi, List <dynamic> githubEvents) { var output = new Dictionary <string, GithubActor>(); var logins = githubEvents.Select(e => e.actor.login.Value.ToString()).Distinct().ToList(); foreach (string login in logins) { var apiResult = githubApi.GetUser(login); var actor = new GithubActor() { Login = login, Name = apiResult.Data.name.Value }; output.Add(login, actor); } return(output); }
private static GithubFollowee GetUserFollowees(string username, GithubApi githubApi, string etag) { var output = new GithubFollowee(); output.Username = username; var followeesApiResults = githubApi.GetUserFollowees(username, etag); if (followeesApiResults.Any() == true) { output.Etag = followeesApiResults.First().Etag; output.Modified = followeesApiResults.First().Modified; var followees = followeesApiResults.SelectMany(r => r.Data); output.Followees = followees.Select(u => (string)u.login.Value).Distinct().ToList(); } return(output); }
public static void Run( [TimerTrigger("0 */10 * * * *")] TimerInfo myTimer, [Blob("data-storage/github.status.json", System.IO.FileAccess.ReadWrite, Connection = "BlobStorageConnection")] CloudBlockBlob githubStatusBlob, TraceWriter log) { log.Info($"C# Timer trigger function executed at: {DateTime.Now}"); // Initialize out API helper class. // As part of the initialization process, it gets GitHub credentials from app settings and "Last Modified" date from blob storage var githubApi = new GithubApi(githubStatusBlob); // Call the GitHub "Received Events" API for the current user and deserialize the JSON that the API sends back var githubEventsApiResults = githubApi.GetReceivedEvents(); var githubEvents = githubEventsApiResults.SelectMany(r => r.Data).ToList(); // The GitHub "Received Events" API no longer includes follow notices (e.g. Frank is now following Susan) // However, it would be nice to have that in our feed... so we have to build it ourselves. // We'll keep track of everyone we follow and who they are following. Then we'll compare the current list of followings // to what we're tracking. If something has changed (i.e. a following is the list that wasn't there before), we can // report that as a new event. // First, get a list of everyone that I am following var me = GetUserFollowees(githubApi.Configuration.Username, githubApi, githubApi.Status.FolloweesEtag); var previousFollowees = githubApi.Status.Followees.Select(f => f.Username); if (me.Modified == false) { // My list of followers has not changed since the last run. So just use the old list from the config me.Followees.AddRange(previousFollowees); } else { // If my list of followers has changed, it could be because I stopped following someone. // In that case, we need to remove them from the config so we don't keep tracking them. // (No need to worry about new additions here. They get handled in the regular processing loop. var drops = previousFollowees.Except(me.Followees); githubApi.Status.Followees.RemoveAll(f => drops.Contains(f.Username)); } // Now loop through all of the I'm following people and get a list of everyone they are following var followees = new List <GithubFollowee>(); foreach (string username in me.Followees) { string etag = ""; var followee = githubApi.Status.Followees.FirstOrDefault(f => f.Username == username); if (followee != null) { etag = followee.Etag; } followees.Add(GetUserFollowees(username, githubApi, etag)); } foreach (var followee in followees) { if (githubApi.Status.Followees.Any(f => f.Username == followee.Username) == false) { // This is a new person that I'm following. Don't tweet about all of their existings follows... // Just add them to the list so we can start watching them. githubApi.Status.Followees.Add(followee); } else { // For everyone that I've been following, compare their new list of followees to the list that we stored // Tweet about any changes in the list. if (followee.Modified == true) { var oldFollowee = githubApi.Status.Followees.First(f => f.Username == followee.Username); var adds = followee.Followees.Except(oldFollowee.Followees).ToList(); var drops = oldFollowee.Followees.Except(followee.Followees).ToList(); // For any changes that we discover, create a "fake" event in our event list adds.ForEach(i => githubEvents.Add(CreateFakeFollowEvent(followee.Username, "started", i))); drops.ForEach(i => githubEvents.Add(CreateFakeFollowEvent(followee.Username, "stopped", i))); // For any changes that we discover, update the status with the changes oldFollowee.Followees.AddRange(adds); oldFollowee.Followees.RemoveAll(f => drops.Contains(f)); oldFollowee.Etag = followee.Etag; } } } // Now that we have followees sorted out, let's continue on with our tweeting... // Build a hash table of all of the actors mentioned in the "Received Events" feed // (Note that it makes an API call for each actor to get their details var actors = GithubActor.GetDistinct(githubApi, githubEvents); // Loop through all of the items in the data we got from the API. If there's a new item we havent seen before, // create a new tweet for it. var tweets = new List <Tweet>(); foreach (var item in githubEvents) { if (item.created_at.Value > githubApi.Status.EventsLastModified) { var actor = actors[item.actor.login.Value]; var tweet = Tweet.Create(actor, item); tweets.Add(tweet); } } // Now it's time to send out our tweets! var twitterConfig = TwitterConfiguration.Retrieve(); var twitterApi = new TwitterApi(twitterConfig); var tweetResults = new List <dynamic>(); foreach (var tweet in tweets) { string resultJson = twitterApi.SendTweet(tweet.CreateBody()); tweetResults.Add(JsonConvert.DeserializeObject <dynamic>(resultJson)); } // Save the updated status back to blob storage githubApi.Status.StoreInBlob(githubStatusBlob, githubEventsApiResults, me); // Report back our results log.Info($"C# Timer trigger function completed at: {DateTime.Now} - attempted {tweets.Count} tweets"); foreach (var tweetResult in tweetResults) { if (tweetResult.errors == null) { log.Info($"--- SUCCESS! {tweetResult.text.Value}"); } else { var error = tweetResult.errors.First; log.Info($"--- FAILED! Code {error.code.Value}: {error.message.Value}"); } } }