Created
February 9, 2015 17:42
-
-
Save Redth/2c8f20293a7152a22afb to your computer and use it in GitHub Desktop.
ThisLife C# API Client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Newtonsoft.Json.Linq; | |
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
namespace ThisLife | |
{ | |
public class ThisLifeClient | |
{ | |
const int MOMENT_LENGTH = 43; | |
const int MOMENT_BATCH_SIZE = 400; | |
const string apiUrl = "http://cmd.thislife.com/json"; | |
public ThisLifeClient () | |
{ | |
} | |
public ThisLifeClient (string authToken) | |
{ | |
AuthToken = authToken; | |
} | |
HttpClient http = new HttpClient (); | |
public string AuthToken { get; private set; } | |
public string LifeUid { get; private set; } | |
public async Task<bool> Authenticate (string email, string password) | |
{ | |
// {"method":"loginWithCredentials","params":[{"email":"[email protected]","source":"airDownloader","password":"12345"}],"headers":["Keep-Alive: 115","Connection: keep-alive"],"id":null} | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams ("Connection: keep-alive", "Keep-Alive: 115"); | |
json ["method"] = "loginWithCredentials"; | |
json ["params"] = BuildParams (new Dictionary<string, string> { | |
{ "email", email }, | |
{ "source", "airDownloader" }, | |
{ "password", password } | |
}); | |
var payload = await ApiCall (json); | |
var authToken = string.Empty; | |
var lifeUid = string.Empty; | |
try { | |
authToken = payload ["sessionToken"].ToString (); | |
var lifePerms = payload ["person"]["life_permissions"] as JArray; | |
lifeUid = lifePerms[0]["life_uid"].ToString (); | |
} | |
catch { | |
} | |
LifeUid = lifeUid; | |
AuthToken = authToken; | |
return !string.IsNullOrEmpty (AuthToken) && !string.IsNullOrEmpty (LifeUid); | |
} | |
public async Task<JObject> GetLifeInfo () | |
{ | |
// {"method":"getLifeInfo","params":["authToken","lifeUid"],"headers":["Keep-Alive: 115","Connection: keep-alive"],"id":null} | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams ("Connection: keep-alive", "Keep-Alive: 115"); | |
json ["method"] = "getLifeInfo"; | |
json ["params"] = BuildParams (AuthToken, LifeUid); | |
return await ApiCall (json); | |
} | |
public async Task<IEnumerable<MomentSummary>> GetStartupItems () | |
{ | |
//{"method":"getStartUpItems","params":["authToken","lifeUid"],"headers":["Keep-Alive: 115","Connection: keep-alive"],"id":null} | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams ("Connection: keep-alive", "Keep-Alive: 115"); | |
json ["method"] = "getStartUpItems"; | |
json ["params"] = BuildParams (AuthToken, LifeUid); | |
var payload = await ApiCall (json); | |
var momentSummaries = ParseMoments (payload.ToString ()); | |
return momentSummaries; | |
} | |
public async Task<IEnumerable<Moment>> GetMoments (IEnumerable<MomentSummary> momentSummaries) | |
{ | |
var moments = new List<Moment> (); | |
foreach (var batch in momentSummaries.Batch (MOMENT_BATCH_SIZE)) { | |
var momentIds = new JArray (); | |
foreach (var m in batch) | |
momentIds.Add (m.Uid); | |
var p = new JArray (); | |
p.Add (AuthToken); | |
p.Add (momentIds); | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams ("Connection: keep-alive", "Keep-Alive: 115"); | |
json ["method"] = "getMomentSet"; | |
json ["params"] = p; | |
var payload = await ApiCall (json); | |
var batchMoments = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Moment>> (payload.ToString ()); | |
moments.AddRange (batchMoments); | |
} | |
return moments; | |
} | |
public async Task<IEnumerable<StorySummary>> GetStories () | |
{ | |
//{"method":"getStories","params":["authToken","lifeUid",true],"headers":{"X-SFLY-SubSource":"library"},"id":null}: | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams (new Dictionary<string, string> { | |
{ "X-SFLY-SubSource", "library" }, | |
}); | |
json ["method"] = "getStories"; | |
json ["params"] = BuildParams (AuthToken, LifeUid, true); | |
var payload = await ApiCallArray (json); | |
var stories = new List<StorySummary> (); | |
foreach (var arr in payload) { | |
foreach (var item in arr) { | |
var uid = item ["story_uid"].ToString (); | |
var name = item ["story"] ["name"].ToString (); | |
stories.Add (new StorySummary { | |
Uid = uid, | |
Name = name | |
}); | |
} | |
} | |
return stories; | |
} | |
public async Task<Story> GetStory (string storyUid) | |
{ | |
//{"method":"getStory","params":["authToken",null],"headers":{"X-SFLY-SubSource":"story"},"id":null}: | |
var json = new JObject (); | |
json ["id"] = null; | |
json ["headers"] = BuildParams (new Dictionary<string, string> { | |
{ "X-SFLY-SubSource", "story" }, | |
}); | |
json ["method"] = "getStory"; | |
json ["params"] = BuildParams (AuthToken, storyUid, null); | |
var payload = await ApiCall (json); | |
var story = new Story { | |
Moments = ParseMoments (payload ["moments"].ToString ()), | |
Uid = payload ["uid"].ToString (), | |
Name = payload ["name"].ToString () | |
}; | |
return story; | |
} | |
List<MomentSummary> ParseMoments (string data) | |
{ | |
var momentSummaries = new List<MomentSummary> (); | |
var i = 0; | |
while (true) { | |
if (data.Length < i + MOMENT_LENGTH) | |
break; | |
var moment = data.Substring (i, MOMENT_LENGTH); | |
//var dateNeg = moment.Substring (0, 1); | |
//var date = moment.Substring (1, 8); | |
var uid = moment.Substring (9, 16); | |
//var modified = moment.Substring (25, 8); | |
//var height = moment.Substring (33, 4); | |
//var width = moment.Substring (37, 4); | |
//var flags = moment.Substring (41, 1); | |
//var momentType = moment.Substring (42); | |
momentSummaries.Add (new MomentSummary { | |
Uid = uid | |
}); | |
i += MOMENT_LENGTH; | |
} | |
return momentSummaries; | |
} | |
JArray BuildParams (params object[] p) | |
{ | |
var json = new JArray (); | |
foreach (var s in p) { | |
json.Add (s); | |
} | |
return json; | |
} | |
JArray BuildParams (Dictionary<string, string> p) | |
{ | |
var json = new JArray (); | |
var obj = new JObject (); | |
foreach (var kvp in p) { | |
obj [kvp.Key] = kvp.Value; | |
} | |
json.Add (obj); | |
return json; | |
} | |
async Task<JObject> ApiCall (JObject request) | |
{ | |
return (await apiCall (request)) as JObject; | |
} | |
async Task<JArray> ApiCallArray (JObject request) | |
{ | |
return (await apiCall (request)) as JArray; | |
} | |
async Task<JToken> apiCall (JObject request) | |
{ | |
http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | |
var requestBody = request.ToString (); | |
var result = await http.PostAsync (apiUrl, new StringContent (requestBody, System.Text.Encoding.UTF8, "application/json")); | |
result.EnsureSuccessStatusCode (); | |
var resultString = await result.Content.ReadAsStringAsync (); | |
var jsonResponse = JObject.Parse (resultString); | |
// Check for errors | |
if (jsonResponse ["result"] == null | |
|| jsonResponse ["result"] ["success"] == null | |
|| jsonResponse ["result"] ["payload"] == null | |
|| !jsonResponse ["result"] ["success"].ToString ().Equals ("true", StringComparison.InvariantCultureIgnoreCase)) { | |
// Get a message if we can from the response | |
var message = "Unknown Error"; | |
try { | |
message = jsonResponse ["result"] ["message"].ToString (); | |
} catch { | |
} | |
throw new ThisLifeException (message); | |
} | |
return jsonResponse ["result"] ["payload"]; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
namespace ThisLife | |
{ | |
public static class LinqExtensions | |
{ | |
public static IEnumerable<IEnumerable<TSource>> Batch<TSource> (this IEnumerable<TSource> source, int size) | |
{ | |
TSource[] bucket = null; | |
var count = 0; | |
foreach (var item in source) { | |
if (bucket == null) | |
bucket = new TSource[size]; | |
bucket [count++] = item; | |
if (count != size) | |
continue; | |
yield return bucket; | |
bucket = null; | |
count = 0; | |
} | |
if (bucket != null && count > 0) | |
yield return bucket.Take (count); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Newtonsoft.Json; | |
namespace ThisLife | |
{ | |
public class Moment | |
{ | |
public Moment () | |
{ | |
} | |
[JsonProperty ("uid")] | |
public string Uid { get; set; } | |
[JsonProperty ("original_filename")] | |
public string OriginalFilename { get;set; } | |
[JsonProperty ("short_text")] | |
public string ShortText { get;set; } | |
[JsonProperty ("note")] | |
public string Note { get;set; } | |
[JsonProperty ("hidden")] | |
public bool Hidden { get;set; } | |
[JsonProperty ("caption_text")] | |
public string CaptionText { get; set; } | |
[JsonProperty ("comment_count")] | |
public int CommentCount { get;set; } | |
[JsonProperty ("lat")] | |
public double Latitude { get;set; } | |
[JsonProperty ("long")] | |
public double Longitude { get;set; } | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ThisLife | |
{ | |
public class MomentSummary | |
{ | |
public MomentSummary () | |
{ | |
} | |
public string Uid { get;set; } | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace ThisLife | |
{ | |
public class Story | |
{ | |
public Story () | |
{ | |
} | |
public string Uid { get;set; } | |
public string Name { get; set; } | |
public List<MomentSummary> Moments { get; set; } | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ThisLife | |
{ | |
public class StorySummary | |
{ | |
public StorySummary () | |
{ | |
} | |
public string Uid { get;set; } | |
public string Name { get;set; } | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ThisLife | |
{ | |
public class ThisLifeException : Exception | |
{ | |
public ThisLifeException (string msg) : base (msg) | |
{ | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment