diff --git a/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj b/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj index 68def30..7222040 100644 --- a/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj +++ b/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj @@ -67,8 +67,8 @@ ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.Extensions.dll - - ..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll @@ -129,6 +129,10 @@ {1EDCF34E-E1B1-4F82-AEC7-90C35A267967} Habitica.Todoist.Integration.Model + + {A804D4CC-B5CC-466F-AF3D-E850B16D2D15} + Habitica.Todoist.Integration.Services + diff --git a/Habitica.Todoist.Integration.Console/Program.cs b/Habitica.Todoist.Integration.Console/Program.cs index b03f8bd..3c246fe 100644 --- a/Habitica.Todoist.Integration.Console/Program.cs +++ b/Habitica.Todoist.Integration.Console/Program.cs @@ -1,5 +1,6 @@ using Habitica.Todoist.Integration.Model.Habitica; using Habitica.Todoist.Integration.Model.Todoist; +using Habitica.Todoist.Integration.Services; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using System; @@ -27,17 +28,10 @@ namespace Habitica.Todoist.Integration.Console { ConfigBuild(); - SyncResponse response = null; - using (var client = new WebClient()) - { - client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; - var token = "*"; - var body = $"token={todoistApiKey}&sync_token={token}&resource_types=[\"items\"]"; - response = JsonConvert.DeserializeObject(client.UploadString($"{todoistApiUrl}sync", body)); - } - - - foreach (var item in response.Items) + var todoistClient = new TodoistClientService(todoistApiKey); + var syncResponse = todoistClient.GetUpdatedItems().ConfigureAwait(false).GetAwaiter().GetResult(); + + foreach (var item in syncResponse.Items) { var newTask = new Task { @@ -47,18 +41,8 @@ namespace Habitica.Todoist.Integration.Console Priority = GetHabiticaDifficulty(item.GetDifficulty().GetValueOrDefault()) }; - using (var client = new WebClient()) - { - client.Headers["Content-Type"] = "application/json"; - client.Headers["x-api-user"] = habiticaUserId; - client.Headers["x-api-key"] = habiticaApiKey; - client.Headers["x-client"] = "console-test"; - - var result = client.UploadString($"{habiticaApiUrl}tasks/user", "POST", JsonConvert.SerializeObject(newTask, Formatting.Indented)); - - System.Console.WriteLine(result); - System.Console.ReadKey(); - } + var habiticaClient = new HabiticaClientService(habiticaUserId, habiticaApiKey); + var task = habiticaClient.CreateUserTask(newTask).ConfigureAwait(false).GetAwaiter().GetResult(); } } diff --git a/Habitica.Todoist.Integration.Console/packages.config b/Habitica.Todoist.Integration.Console/packages.config index e01460a..ba66f62 100644 --- a/Habitica.Todoist.Integration.Console/packages.config +++ b/Habitica.Todoist.Integration.Console/packages.config @@ -13,7 +13,7 @@ - + diff --git a/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj b/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj index d70db86..6e86919 100644 --- a/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj +++ b/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj @@ -5,7 +5,7 @@ - + diff --git a/Habitica.Todoist.Integration.Model/Habitica/Task.cs b/Habitica.Todoist.Integration.Model/Habitica/Task.cs index 88824dd..192b1b9 100644 --- a/Habitica.Todoist.Integration.Model/Habitica/Task.cs +++ b/Habitica.Todoist.Integration.Model/Habitica/Task.cs @@ -15,5 +15,7 @@ namespace Habitica.Todoist.Integration.Model.Habitica public string Date { get; set; } [JsonProperty("priority")] public string Priority { get; set; } + [JsonProperty("id")] + public string Id { get; set; } } } diff --git a/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs b/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs new file mode 100644 index 0000000..0d6bb28 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public class HabitTodoLink + { + public string HabiticaId { get; set; } + public string TodoistId { get; set; } + } +} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoAction.cs b/Habitica.Todoist.Integration.Model/Storage/TodoAction.cs new file mode 100644 index 0000000..00fff30 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/TodoAction.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public enum TodoAction + { + Add = 1, + Update = 2, + Complete = 3, + Delete = 4 + } +} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoApp.cs b/Habitica.Todoist.Integration.Model/Storage/TodoApp.cs new file mode 100644 index 0000000..0f81a11 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/TodoApp.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public enum TodoApp + { + Habitica = 1, + Todoist = 2 + } +} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs b/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs new file mode 100644 index 0000000..7e670e4 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public class TodoChange + { + public string Id { get; set; } + public TodoApp Application { get; set; } + public TodoAction Action { get; set; } + public bool Applied { get; set; } + public string JsonTodo { get; set; } + } +} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs b/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs new file mode 100644 index 0000000..0394d52 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public class TodoHabitLink + { + public string TodoistId { get; set; } + public string HabiticaId { get; set; } + } +} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs b/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs new file mode 100644 index 0000000..4264baf --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Storage +{ + public class TodoistSync + { + public string SyncToken { get; set; } + } +} diff --git a/Habitica.Todoist.Integration.Model/Todoist/Due.cs b/Habitica.Todoist.Integration.Model/Todoist/Due.cs index 357f386..43fd2d8 100644 --- a/Habitica.Todoist.Integration.Model/Todoist/Due.cs +++ b/Habitica.Todoist.Integration.Model/Todoist/Due.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.Globalization; using System.Text; @@ -7,8 +8,11 @@ namespace Habitica.Todoist.Integration.Model.Todoist { public class Due { + [JsonProperty("date")] public string Date { get; set; } + [JsonProperty("timezone")] public string Timezone { get; set; } + [JsonProperty("string")] public string @String { get; set; } public string ToJavaScriptDateStr() diff --git a/Habitica.Todoist.Integration.Model/Todoist/Item.cs b/Habitica.Todoist.Integration.Model/Todoist/Item.cs index 238eae8..215c59d 100644 --- a/Habitica.Todoist.Integration.Model/Todoist/Item.cs +++ b/Habitica.Todoist.Integration.Model/Todoist/Item.cs @@ -1,14 +1,20 @@ -using System; +using Newtonsoft.Json; +using System; using System.Linq; namespace Habitica.Todoist.Integration.Model.Todoist { public class Item { + [JsonProperty("content")] public string Content { get; set; } + [JsonProperty("Id")] public string Id { get; set; } + [JsonProperty("due")] public Due Due { get; set; } + [JsonProperty("is_deleted")] public int Is_deleted { get; set; } + [JsonProperty("date_completed")] public string Date_completed { get; set; } public int? GetDifficulty() diff --git a/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs b/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs index 6548371..ffcf01f 100644 --- a/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs +++ b/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.Text; @@ -6,8 +7,11 @@ namespace Habitica.Todoist.Integration.Model.Todoist { public class SyncResponse { + [JsonProperty("sync_token")] public string Sync_token { get; set; } + [JsonProperty("full_sync")] public bool Full_sync { get; set; } + [JsonProperty("itmes")] public List Items { get; set; } } } diff --git a/Habitica.Todoist.Integration.Services/Class1.cs b/Habitica.Todoist.Integration.Services/Class1.cs deleted file mode 100644 index 4087b36..0000000 --- a/Habitica.Todoist.Integration.Services/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Habitica.Todoist.Integration.Apis -{ - public class Class1 - { - } -} diff --git a/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs b/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs new file mode 100644 index 0000000..cdb7ac4 --- /dev/null +++ b/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs @@ -0,0 +1,23 @@ +using Habitica.Todoist.Integration.Model.Storage; +using Microsoft.Azure.Cosmos.Table; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Services +{ + public class HabitTodoStorageClient + { + private CloudStorageAccount storageAccount { get; set; } + + public HabitTodoStorageClient(string connectionString) + { + this.storageAccount = CloudStorageAccount.Parse(connectionString); + } + + //public TodoLink CreateTodoLink(TodoLink todoLink) + //{ + // return null; + //} + } +} diff --git a/Habitica.Todoist.Integration.Services/Habitica.Todoist.Integration.Services.csproj b/Habitica.Todoist.Integration.Services/Habitica.Todoist.Integration.Services.csproj index 9f5c4f4..2ac2fc5 100644 --- a/Habitica.Todoist.Integration.Services/Habitica.Todoist.Integration.Services.csproj +++ b/Habitica.Todoist.Integration.Services/Habitica.Todoist.Integration.Services.csproj @@ -4,4 +4,13 @@ netstandard2.0 + + + + + + + + + diff --git a/Habitica.Todoist.Integration.Services/HabiticaClientService.cs b/Habitica.Todoist.Integration.Services/HabiticaClientService.cs new file mode 100644 index 0000000..823b80f --- /dev/null +++ b/Habitica.Todoist.Integration.Services/HabiticaClientService.cs @@ -0,0 +1,45 @@ +using HabiticaTask = Habitica.Todoist.Integration.Model.Habitica.Task; +using System; +using System.Net; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Habitica.Todoist.Integration.Services +{ + public class HabiticaClientService + { + private string userId { get; set; } + private string apiKey { get; set; } + + private string baseUrl => "https://habitica.com/api/v3/"; + + public HabiticaClientService(string userId, string apiKey) + { + this.userId = userId; + this.apiKey = apiKey; + } + + public async Task CreateUserTask(HabiticaTask task) + { + using (var client = CreateWebClient()) + { + var request = JsonConvert.SerializeObject(task); + var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/user", "POST", request); + + return JsonConvert.DeserializeObject(json); + } + } + + private WebClient CreateWebClient() + { + var client = new WebClient(); + + client.Headers[HttpRequestHeader.ContentType] = "application/json"; + client.Headers["x-api-user"] = userId; + client.Headers["x-api-key"] = apiKey; + client.Headers["x-client"] = "dotnet-habitica-client"; + + return client; + } + } +} diff --git a/Habitica.Todoist.Integration.Services/TodoistClientService.cs b/Habitica.Todoist.Integration.Services/TodoistClientService.cs new file mode 100644 index 0000000..c886c10 --- /dev/null +++ b/Habitica.Todoist.Integration.Services/TodoistClientService.cs @@ -0,0 +1,59 @@ +using Habitica.Todoist.Integration.Model.Todoist; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Habitica.Todoist.Integration.Services +{ + public class TodoistClientService + { + private string apiKey { get; set; } + private string baseUrl => "https://api.todoist.com/sync/v8/"; + + public TodoistClientService(string apiKey) + { + this.apiKey = apiKey; + } + + public async Task GetUpdatedItems(string syncToken = "*") + { + using (var client = CreateWebClient()) + { + var body = InitializeRequestBody(); + body["sync_token"] = syncToken; + body["resource_types"] = "[\"items\"]"; + + var json = await client.UploadStringTaskAsync($"{baseUrl}sync", RequestBodyToString(body)); + return JsonConvert.DeserializeObject(json); + } + } + + private string RequestBodyToString(Dictionary body) + { + var bodyStr = ""; + foreach (var pair in body) + bodyStr += $"{pair.Key}={pair.Value}&"; + + return bodyStr; + } + + private Dictionary InitializeRequestBody() + { + var body = new Dictionary(); + body["token"] = apiKey; + + return body; + } + + private WebClient CreateWebClient() + { + var client = new WebClient(); + client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; + + return client; + } + } +}