From be026bfdca02707233dc098cf16c565e655c4b54 Mon Sep 17 00:00:00 2001 From: guitarrapc Date: Tue, 23 May 2017 21:02:12 +0900 Subject: [PATCH] Remove nuget library chatwork.api dependency to resolve .NET Core 1.0 strict version hold. --- .../SendToChatwork/ChatworkClient.cs | 379 ++++++++++++++++++ .../SendToChatwork/ChatworkModels.cs | 166 ++++++++ .../SendToChatwork/DateTimeExtensions.cs | 53 +++ .../SendToChatwork/SendToChatwork.csproj | 1 - 4 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 src/SendToChatwork/SendToChatwork/ChatworkClient.cs create mode 100644 src/SendToChatwork/SendToChatwork/ChatworkModels.cs create mode 100644 src/SendToChatwork/SendToChatwork/DateTimeExtensions.cs diff --git a/src/SendToChatwork/SendToChatwork/ChatworkClient.cs b/src/SendToChatwork/SendToChatwork/ChatworkClient.cs new file mode 100644 index 0000000..7568aaa --- /dev/null +++ b/src/SendToChatwork/SendToChatwork/ChatworkClient.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Chatwork.Service +{ + public class ChatWorkAuthenticationHandler : DelegatingHandler + { + readonly string apiToken; + + public ChatWorkAuthenticationHandler(string apiToken) + : this(apiToken, new System.Net.Http.HttpClientHandler()) + { } + + public ChatWorkAuthenticationHandler(string apiToken, HttpMessageHandler innerHandler) + : base(innerHandler) + { + this.apiToken = apiToken; + } + + protected override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) + { + request.Headers.Add("X-ChatWorkToken", apiToken); + return base.SendAsync(request, cancellationToken); + } + } + + public partial class ChatworkClient : IMe, IMy, IContact, IRoom + { + private static readonly string BaseUri = "https://api.chatwork.com/v2/"; + private static readonly int ContentBodyKeyValueLimit = 30000; + + readonly HttpClient httpClient; + + public long MaxResponseContentBufferSize + { + get + { + return httpClient.MaxResponseContentBufferSize; + } + set + { + httpClient.MaxResponseContentBufferSize = value; + } + } + + public TimeSpan Timeout + { + get + { + return httpClient.Timeout; + } + set + { + httpClient.Timeout = value; + } + } + + public int Limit { get; set; } + public int RemainingLimit { get; set; } + public DateTime ResetTime { get; set; } + + public IMe Me => this; + public IMy My => this; + public IContact Contract => this; + public IRoom Room => this; + + public ChatworkClient(string apiToken) + { + httpClient = new HttpClient(new ChatWorkAuthenticationHandler(apiToken)); + } + + public ChatworkClient(string apiToken, HttpMessageHandler innerHandler) + { + httpClient = new HttpClient(new ChatWorkAuthenticationHandler(apiToken, innerHandler)); + } + + private Task GetAsync(string path, params KeyValuePair[] parameters) + { + //TODO エスケープ処理 + var requestUri = new Uri(BaseUri + path + "?" + string.Join("&", parameters.Where(p => p.Value != null).Select(p => p.Key + "=" + ConvertToString(p.Value)))); + var request = new HttpRequestMessage() + { + Method = HttpMethod.Get, + RequestUri = requestUri + }; + return SendAsync(request); + } + + private Task SendAsync(HttpMethod method, string path, params KeyValuePair[] parameters) + { + var request = new HttpRequestMessage() + { + Method = method, + Content = new FormUrlEncodedContent(parameters.Where(p => p.Value != null).Select(p => new KeyValuePair(p.Key, ConvertToString(p.Value)))), + RequestUri = new Uri(BaseUri + path) + }; + return SendAsync(request); + } + + private async Task SendAsync(HttpRequestMessage request) + { + var res = await httpClient.SendAsync(request).ConfigureAwait(false); + UpdateCallLimit(res); + if (res.IsSuccessStatusCode) + { + return JsonConvert.DeserializeObject(await res.Content.ReadAsStringAsync().ConfigureAwait(false)); + } + throw new Exception($"Failed with code {res.StatusCode}. Message: {await res.Content.ReadAsStringAsync()}"); + } + + private async Task SendAsync(HttpMethod httpMethod, string path, params KeyValuePair[] parameters) + { + var request = new HttpRequestMessage() + { + Method = httpMethod, + Content = new FormUrlEncodedContent(parameters.Where(p => p.Value != null).Select(p => new KeyValuePair(p.Key, ConvertToString(p.Value)))), + RequestUri = new Uri(BaseUri + path) + }; + var res = await httpClient.SendAsync(request).ConfigureAwait(false); + UpdateCallLimit(res); + if (res.IsSuccessStatusCode) + { + return; + } + throw new Exception($"Failed with code {res.StatusCode}. Message: {await res.Content.ReadAsStringAsync()}"); + } + + private string ConvertToString(object value) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + var ints = value as IEnumerable; + if (ints != null) + { + return string.Join(",", ints); + } + var dt = value as DateTime?; + if (dt.HasValue) + { + return dt.Value.ToUnixTime().ToString(); + } + var flg = value as bool?; + if (flg.HasValue) + { + return flg.Value ? "1" : "0"; + } + return value.ToString(); + } + + private void UpdateCallLimit(HttpResponseMessage res) + { + Limit = int.Parse(res.Headers.GetValues("X-RateLimit-Limit").Single()); + RemainingLimit = int.Parse(res.Headers.GetValues("X-RateLimit-Remaining").Single()); + ResetTime = DateTimeExtensions.FromUnixTime(long.Parse(res.Headers.GetValues("X-RateLimit-Reset").Single())); + } + } + + public interface IMe + { + Task GetAsync(); + } + + public partial class ChatworkClient //IMe + { + Task IMe.GetAsync() + { + return GetAsync("/me"); + } + } + + public interface IMy + { + Task GetStatusAsync(); + + Task> GetTasksAsync(int? assigned_by_account_id = null, string status = null); + } + + public partial class ChatworkClient //IMy + { + Task IMy.GetStatusAsync() + { + return GetAsync("/my/status"); + } + + async Task> IMy.GetTasksAsync(int? assigned_by_account_id, string status) + { + var res = await GetAsync>("/my/tasks" + , new KeyValuePair("assigned_by_account_id", assigned_by_account_id) + , new KeyValuePair("status", status)); + return res ?? Enumerable.Empty().ToList(); + } + } + + public interface IContact + { + Task> GetAsync(); + } + + public partial class ChatworkClient // IContact + { + async Task> IContact.GetAsync() + { + var res = await GetAsync>("/contacts"); + return res ?? Enumerable.Empty().ToList(); + } + } + + public interface IRoom + { + Task> GetAsync(); + Task CreateAsync(IEnumerable members_admin_ids, + string name, + string description = null, + string icon_preset = null, + IEnumerable members_member_ids = null, + IEnumerable members_readonly_ids = null); + Task GetRoomAsync(int room_id); + Task UpdateRoomAsync(int room_id, + string name, + string description = null, + string icon_preset = null); + Task LeaveRoomAsync(int room_id, + string action_type); + Task> GetRoomMembersAsync(int room_id); + Task UpdateRoomMembersAsync(int room_id, + IEnumerable members_admin_ids, + IEnumerable members_member_ids = null, + IEnumerable members_readonly_ids = null); + Task> GetMessagesAsync(int room_id, bool force = false); + Task SendMessgesAsync(int room_id, string body); + Task GetMessageAsync(int room_id, long message_id); + Task> GetTasksAsync(int room_id, int? account_id = null, int? assigned_by_account_id = null, string status = null); + Task CreateTasksAsync(int room_id, + string body, + IEnumerable to_ids, + DateTime? limit = null); + Task GetTaskInfoAsync(int room_id, int task_id); + Task> GetFilesAsync(int room_id); + Task GetFilAsync(int room_id, int file_id, bool create_download_url = false); + } + + public partial class ChatworkClient // IRoom + { + async Task> IRoom.GetAsync() + { + var res = await GetAsync>("/rooms"); + return res ?? Enumerable.Empty().ToList(); + } + + Task IRoom.CreateAsync(IEnumerable members_admin_ids, + string name, + string description, + string icon_preset, + IEnumerable members_member_ids, + IEnumerable members_readonly_ids) + { + return SendAsync( + HttpMethod.Post, + "/rooms", + new KeyValuePair("members_admin_ids", members_admin_ids), + new KeyValuePair("name", name), + new KeyValuePair("icon_preset", icon_preset), + new KeyValuePair("members_member_ids", members_member_ids), + new KeyValuePair("members_readonly_ids", members_readonly_ids)); + } + + Task IRoom.GetRoomAsync(int room_id) + { + return GetAsync("/rooms/" + room_id); + } + + Task IRoom.UpdateRoomAsync(int room_id, + string name, + string description, + string icon_preset) + { + return SendAsync( + HttpMethod.Put, + "/rooms/" + room_id, + new KeyValuePair("description", description), + new KeyValuePair("name", name), + new KeyValuePair("icon_preset", icon_preset)); + } + + Task IRoom.LeaveRoomAsync(int room_id, + string action_type) + { + return SendAsync( + HttpMethod.Delete, + "/rooms/" + room_id, + new KeyValuePair("action_type", action_type)); + } + + async Task> IRoom.GetRoomMembersAsync(int room_id) + { + var res = await GetAsync>("/rooms/" + room_id + "/members"); + return res ?? Enumerable.Empty().ToList(); + } + + Task IRoom.UpdateRoomMembersAsync(int room_id, + IEnumerable members_admin_ids, + IEnumerable members_member_ids, + IEnumerable members_readonly_ids) + { + return SendAsync(HttpMethod.Put, + "/rooms/" + room_id + "/members", + new KeyValuePair("members_admin_ids", members_admin_ids), + new KeyValuePair("members_member_ids", members_member_ids), + new KeyValuePair("members_readonly_ids", members_readonly_ids)); + } + + async Task> IRoom.GetMessagesAsync(int room_id, bool force) + { + var res = await GetAsync>("/rooms/" + room_id + "/messages", + new KeyValuePair("force", force)); + return res ?? Enumerable.Empty().ToList(); + } + + Task IRoom.SendMessgesAsync(int room_id, string body) + { + if (body.Length > ContentBodyKeyValueLimit) + { + body = body.Substring(0, ContentBodyKeyValueLimit) + "\r\n [メッセージが長すぎるため省略されました]"; + } + return SendAsync(HttpMethod.Post, + "/rooms/" + room_id + "/messages", + new KeyValuePair("body", body)); + } + + Task IRoom.GetMessageAsync(int room_id, long message_id) + { + return GetAsync("/rooms/" + room_id + "/messages/" + message_id); + } + + async Task> IRoom.GetTasksAsync(int room_id, int? account_id, int? assigned_by_account_id, string status) + { + var res = await GetAsync>("/rooms/" + room_id + "/tasks" + , new KeyValuePair("account_id", account_id) + , new KeyValuePair("assigned_by_account_id", assigned_by_account_id) + , new KeyValuePair("status", status)); + return res ?? Enumerable.Empty().ToList(); + } + + Task IRoom.CreateTasksAsync(int room_id, + string body, + IEnumerable to_ids, + DateTime? limit) + { + return SendAsync(HttpMethod.Post + , "/rooms/" + room_id + "/tasks" + , new KeyValuePair("body", body) + , new KeyValuePair("to_ids", to_ids) + , new KeyValuePair("limit", limit)); + } + + Task IRoom.GetTaskInfoAsync(int room_id, int task_id) + { + return GetAsync("/rooms/" + room_id + "/tasks/" + task_id); + } + + async Task> IRoom.GetFilesAsync(int room_id) + { + var res = await GetAsync>("/rooms/" + room_id + "/files"); + return res ?? Enumerable.Empty().ToList(); + } + + Task IRoom.GetFilAsync(int room_id, int file_id, bool create_download_url) + { + return GetAsync("/rooms/" + room_id + "/files/" + file_id + , new KeyValuePair("create_download_url", create_download_url)); + } + } +} diff --git a/src/SendToChatwork/SendToChatwork/ChatworkModels.cs b/src/SendToChatwork/SendToChatwork/ChatworkModels.cs new file mode 100644 index 0000000..fb7216a --- /dev/null +++ b/src/SendToChatwork/SendToChatwork/ChatworkModels.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using Newtonsoft.Json; + +namespace Chatwork.Service +{ + + public class MeModel + { + public int account_id { get; set; } + public int room_id { get; set; } + public string name { get; set; } + public string chatwork_id { get; set; } + public int organization_id { get; set; } + public string organization_name { get; set; } + public string department { get; set; } + public string title { get; set; } + public string url { get; set; } + public string introduction { get; set; } + public string mail { get; set; } + public string tel_organization { get; set; } + public string tel_extension { get; set; } + public string tel_mobile { get; set; } + public string skype { get; set; } + public string facebook { get; set; } + public string twitter { get; set; } + public string avatar_image_url { get; set; } + } + + + public class MyStatusModel + { + public int unread_room_num { get; set; } + public int mention_room_num { get; set; } + public int mytask_room_num { get; set; } + public int unread_num { get; set; } + public int mention_num { get; set; } + public int mytask_num { get; set; } + } + + + public class MyTaskModel + { + public int task_id { get; set; } + public RoomSummaryModel room { get; set; } + public AccountModel assigned_by_account { get; set; } + public int message_id { get; set; } + public string body { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? limit_time { get; set; } + public string status { get; set; } + } + + public class RoomSummaryModel + { + public int room_id { get; set; } + public string name { get; set; } + public string icon_path { get; set; } + } + + public class ContactModel + { + public int account_id { get; set; } + public int room_id { get; set; } + public string name { get; set; } + public string chatwork_id { get; set; } + public int organization_id { get; set; } + public string organization_name { get; set; } + public string department { get; set; } + public string avatar_image_url { get; set; } + } + + public class CreatedRoomModel + { + public int room_id { get; set; } + } + + + public class RoomModel + { + public int room_id { get; set; } + public string name { get; set; } + public string type { get; set; } + public string role { get; set; } + public bool sticky { get; set; } + public int unread_num { get; set; } + public int mention_num { get; set; } + public int mytask_num { get; set; } + public int message_num { get; set; } + public int file_num { get; set; } + public int task_num { get; set; } + public string icon_path { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? last_update_time { get; set; } + } + + public class UpdatedRoomMembersModel + { + public int[] admin { get; set; } + public int[] member { get; set; } + public int[] _readonly { get; set; } + } + + public class CreatedMessageModel + { + public int message_id { get; set; } + } + + public class MessageModel + { + public int message_id { get; set; } + public AccountModel account { get; set; } + public string body { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? send_time { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? update_time { get; set; } + } + + public class TaskModel + { + public int task_id { get; set; } + public RoomModel room { get; set; } + public AccountModel account { get; set; } + public AccountModel assigned_by_account { get; set; } + public int message_id { get; set; } + public string body { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? limit_time { get; set; } + public string status { get; set; } + } + + public class AccountModel + { + public int account_id { get; set; } + public string name { get; set; } + public string avatar_image_url { get; set; } + } + + public class CreatedTasksModel + { + public int[] task_ids { get; set; } + } + + public class FileModel + { + public int file_id { get; set; } + public AccountModel account { get; set; } + public int message_id { get; set; } + public string filename { get; set; } + public int filesize { get; set; } + [JsonProperty] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? upload_time { get; set; } + public string download_url { get; set; } + } + +} diff --git a/src/SendToChatwork/SendToChatwork/DateTimeExtensions.cs b/src/SendToChatwork/SendToChatwork/DateTimeExtensions.cs new file mode 100644 index 0000000..296b400 --- /dev/null +++ b/src/SendToChatwork/SendToChatwork/DateTimeExtensions.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Chatwork.Service +{ + public static class DateTimeExtensions + { + internal static readonly DateTime BaseDate = + new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + public static long ToUnixTime(this DateTime date) + { + var delta = date - BaseDate; + if (delta.TotalSeconds < 0) + { + throw new ArgumentOutOfRangeException(nameof(date), "Unix epoc starts January 1st, 1970"); + } + return (long)delta.TotalSeconds; + } + + public static DateTime FromUnixTime(long unixTime) + { + return BaseDate.AddSeconds(unixTime).ToLocalTime(); + } + } + + public class UnixDateTimeConverter : DateTimeConverterBase + { + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.Integer) + { + throw new Exception( + $"Unexpected token parsing date. Expected Integer, got {reader.TokenType}."); + } + + var ticks = (long)reader.Value; + return ticks == 0 ? default(DateTime?) : DateTimeExtensions.BaseDate.AddSeconds(ticks); + } + + public override void WriteJson(JsonWriter writer, object value, + JsonSerializer serializer) + { + var dt = value as DateTime?; + var ticks = dt?.ToUnixTime() ?? 0; + writer.WriteValue(ticks); + } + } +} diff --git a/src/SendToChatwork/SendToChatwork/SendToChatwork.csproj b/src/SendToChatwork/SendToChatwork/SendToChatwork.csproj index 5a5e6de..d31fefd 100644 --- a/src/SendToChatwork/SendToChatwork/SendToChatwork.csproj +++ b/src/SendToChatwork/SendToChatwork/SendToChatwork.csproj @@ -24,7 +24,6 @@ All -