OSDN Git Service

HttpTwitter.SendDirectMessageメソッドをTwitterApiクラスに置き換え
[opentween/open-tween.git] / OpenTween / Connection / TwitterApiConnection.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Net;
26 using System.Net.Http;
27 using System.Runtime.Serialization;
28 using System.Text;
29 using System.Threading.Tasks;
30 using OpenTween.Api;
31 using OpenTween.Api.DataModel;
32
33 namespace OpenTween.Connection
34 {
35     public class TwitterApiConnection : IApiConnection, IDisposable
36     {
37         public Uri RestApiBase { get; set; } = new Uri("https://api.twitter.com/1.1/");
38
39         public bool IsDisposed { get; private set; } = false;
40
41         public string AccessToken { get; }
42         public string AccessSecret { get; }
43
44         internal HttpClient http;
45
46         public TwitterApiConnection(string accessToken, string accessSecret)
47         {
48             this.AccessToken = accessToken;
49             this.AccessSecret = accessSecret;
50
51             this.InitializeHttpClient(accessToken, accessSecret);
52             Networking.WebProxyChanged += this.Networking_WebProxyChanged;
53         }
54
55         public Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param)
56             => this.GetAsync<T>(uri, param, null);
57
58         public async Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param, string endpointName)
59         {
60             var requestUri = new Uri(this.RestApiBase, uri);
61
62             if (param != null)
63                 requestUri = new Uri(requestUri, "?" + MyCommon.BuildQueryString(param));
64
65             var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
66
67             try
68             {
69                 using (var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
70                     .ConfigureAwait(false))
71                 {
72                     await this.CheckStatusCode(response)
73                         .ConfigureAwait(false);
74
75                     if (endpointName != null)
76                         MyCommon.TwitterApiInfo.UpdateFromHeader(response.Headers, endpointName);
77
78                     using (var content = response.Content)
79                     {
80                         var responseText = await content.ReadAsStringAsync()
81                             .ConfigureAwait(false);
82
83                         try
84                         {
85                             return MyCommon.CreateDataFromJson<T>(responseText);
86                         }
87                         catch (SerializationException ex)
88                         {
89                             throw TwitterApiException.CreateFromException(ex, responseText);
90                         }
91                     }
92                 }
93             }
94             catch (OperationCanceledException ex)
95             {
96                 throw TwitterApiException.CreateFromException(ex);
97             }
98         }
99
100         public async Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string> param)
101         {
102             var requestUri = new Uri(this.RestApiBase, uri);
103             var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
104
105             using (var postContent = new FormUrlEncodedContent(param))
106             {
107                 request.Content = postContent;
108
109                 HttpResponseMessage response = null;
110                 try
111                 {
112                     response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
113                         .ConfigureAwait(false);
114
115                     await this.CheckStatusCode(response)
116                         .ConfigureAwait(false);
117
118                     var result = new LazyJson<T>(response);
119                     response = null;
120
121                     return result;
122                 }
123                 catch (OperationCanceledException ex)
124                 {
125                     throw TwitterApiException.CreateFromException(ex);
126                 }
127                 finally
128                 {
129                     response?.Dispose();
130                 }
131             }
132         }
133
134         public async Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string> param, IDictionary<string, IMediaItem> media)
135         {
136             var requestUri = new Uri(this.RestApiBase, uri);
137             var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
138
139             using (var postContent = new MultipartFormDataContent())
140             {
141                 foreach (var kv in param)
142                     postContent.Add(new StringContent(kv.Value), kv.Key);
143
144                 foreach (var kv in media)
145                     postContent.Add(new StreamContent(kv.Value.OpenRead()), kv.Key);
146
147                 request.Content = postContent;
148
149                 HttpResponseMessage response = null;
150                 try
151                 {
152                     response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
153                         .ConfigureAwait(false);
154
155                     await this.CheckStatusCode(response)
156                         .ConfigureAwait(false);
157
158                     var result = new LazyJson<T>(response);
159                     response = null;
160
161                     return result;
162                 }
163                 catch (OperationCanceledException ex)
164                 {
165                     throw TwitterApiException.CreateFromException(ex);
166                 }
167                 finally
168                 {
169                     response?.Dispose();
170                 }
171             }
172         }
173
174         protected async Task CheckStatusCode(HttpResponseMessage response)
175         {
176             var statusCode = response.StatusCode;
177             if (statusCode == HttpStatusCode.OK)
178             {
179                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
180                 return;
181             }
182
183             string responseText;
184             using (var content = response.Content)
185             {
186                 responseText = await content.ReadAsStringAsync()
187                     .ConfigureAwait(false);
188             }
189
190             if (string.IsNullOrWhiteSpace(responseText))
191             {
192                 if (statusCode == HttpStatusCode.Unauthorized)
193                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
194
195                 throw new TwitterApiException(statusCode, responseText);
196             }
197
198             try
199             {
200                 var error = TwitterError.ParseJson(responseText);
201
202                 if (error?.Errors == null || error.Errors.Length == 0)
203                     throw new TwitterApiException(statusCode, responseText);
204
205                 var errorCodes = error.Errors.Select(x => x.Code);
206                 if (errorCodes.Any(x => x == TwitterErrorCode.InternalError || x == TwitterErrorCode.SuspendedAccount))
207                 {
208                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
209                 }
210
211                 throw new TwitterApiException(error, responseText);
212             }
213             catch (SerializationException)
214             {
215                 throw new TwitterApiException(statusCode, responseText);
216             }
217         }
218
219         public void Dispose()
220         {
221             this.Dispose(true);
222             GC.SuppressFinalize(this);
223         }
224
225         protected virtual void Dispose(bool disposing)
226         {
227             if (this.IsDisposed)
228                 return;
229
230             this.IsDisposed = true;
231
232             if (disposing)
233             {
234                 Networking.WebProxyChanged -= this.Networking_WebProxyChanged;
235                 this.http.Dispose();
236             }
237         }
238
239         ~TwitterApiConnection()
240         {
241             this.Dispose(false);
242         }
243
244         private void InitializeHttpClient(string accessToken, string accessSecret)
245         {
246             var handler = new OAuthHandler(Networking.CreateHttpClientHandler(),
247                 ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
248                 accessToken, accessSecret);
249
250             this.http = Networking.CreateHttpClient(handler);
251         }
252
253         private void Networking_WebProxyChanged(object sender, EventArgs e)
254         {
255             this.InitializeHttpClient(this.AccessToken, this.AccessSecret);
256         }
257     }
258 }