Skip to content

Instantly share code, notes, and snippets.

@Redth
Created February 9, 2015 17:42
Show Gist options
  • Save Redth/2c8f20293a7152a22afb to your computer and use it in GitHub Desktop.
Save Redth/2c8f20293a7152a22afb to your computer and use it in GitHub Desktop.
ThisLife C# API Client
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"];
}
}
}
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);
}
}
}
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; }
}
}
using System;
namespace ThisLife
{
public class MomentSummary
{
public MomentSummary ()
{
}
public string Uid { get;set; }
}
}
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; }
}
}
using System;
namespace ThisLife
{
public class StorySummary
{
public StorySummary ()
{
}
public string Uid { get;set; }
public string Name { get;set; }
}
}
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