OSDN Git Service

HttpClientでOAuth Echoを使用するためのHttpMessageHandlerを実装
[opentween/open-tween.git] / OpenTween / Api / TwitterApi.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.IO;
25 using System.Linq;
26 using System.Text;
27 using System.Threading;
28 using System.Threading.Tasks;
29 using OpenTween.Api.DataModel;
30 using OpenTween.Connection;
31
32 namespace OpenTween.Api
33 {
34     public sealed class TwitterApi : IDisposable
35     {
36         public long CurrentUserId { get; private set; }
37         public string CurrentScreenName { get; private set; }
38
39         public IApiConnection Connection => this.apiConnection;
40
41         internal IApiConnection apiConnection;
42
43         public void Initialize(string accessToken, string accessSecret, long userId, string screenName)
44         {
45             var newInstance = new TwitterApiConnection(accessToken, accessSecret);
46             var oldInstance = Interlocked.Exchange(ref this.apiConnection, newInstance);
47             oldInstance?.Dispose();
48
49             this.CurrentUserId = userId;
50             this.CurrentScreenName = screenName;
51         }
52
53         public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, long? maxId = null, long? sinceId = null)
54         {
55             var endpoint = new Uri("statuses/home_timeline.json", UriKind.Relative);
56             var param = new Dictionary<string, string>
57             {
58                 ["include_entities"] = "true",
59                 ["include_ext_alt_text"] = "true",
60             };
61
62             if (count != null)
63                 param["count"] = count.ToString();
64             if (maxId != null)
65                 param["max_id"] = maxId.ToString();
66             if (sinceId != null)
67                 param["since_id"] = sinceId.ToString();
68
69             return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/home_timeline");
70         }
71
72         public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, long? maxId = null, long? sinceId = null)
73         {
74             var endpoint = new Uri("statuses/mentions_timeline.json", UriKind.Relative);
75             var param = new Dictionary<string, string>
76             {
77                 ["include_entities"] = "true",
78                 ["include_ext_alt_text"] = "true",
79             };
80
81             if (count != null)
82                 param["count"] = count.ToString();
83             if (maxId != null)
84                 param["max_id"] = maxId.ToString();
85             if (sinceId != null)
86                 param["since_id"] = sinceId.ToString();
87
88             return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/mentions_timeline");
89         }
90
91         public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, long? maxId = null, long? sinceId = null)
92         {
93             var endpoint = new Uri("statuses/user_timeline.json", UriKind.Relative);
94             var param = new Dictionary<string, string>
95             {
96                 ["screen_name"] = screenName,
97                 ["include_rts"] = "true",
98                 ["include_entities"] = "true",
99                 ["include_ext_alt_text"] = "true",
100             };
101
102             if (count != null)
103                 param["count"] = count.ToString();
104             if (maxId != null)
105                 param["max_id"] = maxId.ToString();
106             if (sinceId != null)
107                 param["since_id"] = sinceId.ToString();
108
109             return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/user_timeline");
110         }
111
112         public Task<TwitterStatus> StatusesShow(long statusId)
113         {
114             var endpoint = new Uri("statuses/show.json", UriKind.Relative);
115             var param = new Dictionary<string, string>
116             {
117                 ["id"] = statusId.ToString(),
118                 ["include_entities"] = "true",
119                 ["include_ext_alt_text"] = "true",
120             };
121
122             return this.apiConnection.GetAsync<TwitterStatus>(endpoint, param, "/statuses/show/:id");
123         }
124
125         public Task<LazyJson<TwitterStatus>> StatusesUpdate(string status, long? replyToId, IReadOnlyList<long> mediaIds)
126         {
127             var endpoint = new Uri("statuses/update.json", UriKind.Relative);
128             var param = new Dictionary<string, string>
129             {
130                 ["status"] = status,
131                 ["include_entities"] = "true",
132                 ["include_ext_alt_text"] = "true",
133             };
134
135             if (replyToId != null)
136                 param["in_reply_to_status_id"] = replyToId.ToString();
137             if (mediaIds != null && mediaIds.Count > 0)
138                 param.Add("media_ids", string.Join(",", mediaIds));
139
140             return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
141         }
142
143         public Task<LazyJson<TwitterStatus>> StatusesDestroy(long statusId)
144         {
145             var endpoint = new Uri("statuses/destroy.json", UriKind.Relative);
146             var param = new Dictionary<string, string>
147             {
148                 ["id"] = statusId.ToString(),
149             };
150
151             return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
152         }
153
154         public Task<LazyJson<TwitterStatus>> StatusesRetweet(long statusId)
155         {
156             var endpoint = new Uri("statuses/retweet.json", UriKind.Relative);
157             var param = new Dictionary<string, string>
158             {
159                 ["id"] = statusId.ToString(),
160                 ["include_entities"] = "true",
161                 ["include_ext_alt_text"] = "true",
162             };
163
164             return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
165         }
166
167         public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, long? maxId = null, long? sinceId = null, bool? includeRTs = null)
168         {
169             var endpoint = new Uri("lists/statuses.json", UriKind.Relative);
170             var param = new Dictionary<string, string>
171             {
172                 ["list_id"] = listId.ToString(),
173                 ["include_entities"] = "true",
174                 ["include_ext_alt_text"] = "true",
175             };
176
177             if (count != null)
178                 param["count"] = count.ToString();
179             if (maxId != null)
180                 param["max_id"] = maxId.ToString();
181             if (sinceId != null)
182                 param["since_id"] = sinceId.ToString();
183             if (includeRTs != null)
184                 param["include_rts"] = includeRTs.Value ? "true" : "false";
185
186             return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/lists/statuses");
187         }
188
189         public Task<TwitterDirectMessage[]> DirectMessagesRecv(int? count = null, long? maxId = null, long? sinceId = null)
190         {
191             var endpoint = new Uri("direct_messages.json", UriKind.Relative);
192             var param = new Dictionary<string, string>
193             {
194                 ["full_text"] = "true",
195                 ["include_entities"] = "true",
196                 ["include_ext_alt_text"] = "true",
197             };
198
199             if (count != null)
200                 param["count"] = count.ToString();
201             if (maxId != null)
202                 param["max_id"] = maxId.ToString();
203             if (sinceId != null)
204                 param["since_id"] = sinceId.ToString();
205
206             return this.apiConnection.GetAsync<TwitterDirectMessage[]>(endpoint, param, "/direct_messages");
207         }
208
209         public Task<TwitterDirectMessage[]> DirectMessagesSent(int? count = null, long? maxId = null, long? sinceId = null)
210         {
211             var endpoint = new Uri("direct_messages/sent.json", UriKind.Relative);
212             var param = new Dictionary<string, string>
213             {
214                 ["full_text"] = "true",
215                 ["include_entities"] = "true",
216                 ["include_ext_alt_text"] = "true",
217             };
218
219             if (count != null)
220                 param["count"] = count.ToString();
221             if (maxId != null)
222                 param["max_id"] = maxId.ToString();
223             if (sinceId != null)
224                 param["since_id"] = sinceId.ToString();
225
226             return this.apiConnection.GetAsync<TwitterDirectMessage[]>(endpoint, param, "/direct_messages/sent");
227         }
228
229         public Task<LazyJson<TwitterDirectMessage>> DirectMessagesNew(string status, string sendTo)
230         {
231             var endpoint = new Uri("direct_messages/new.json", UriKind.Relative);
232             var param = new Dictionary<string, string>
233             {
234                 ["text"] = status,
235                 ["screen_name"] = sendTo,
236             };
237
238             return this.apiConnection.PostLazyAsync<TwitterDirectMessage>(endpoint, param);
239         }
240
241         public Task<LazyJson<TwitterDirectMessage>> DirectMessagesDestroy(long statusId)
242         {
243             var endpoint = new Uri("direct_messages/destroy.json", UriKind.Relative);
244             var param = new Dictionary<string, string>
245             {
246                 ["id"] = statusId.ToString(),
247             };
248
249             return this.apiConnection.PostLazyAsync<TwitterDirectMessage>(endpoint, param);
250         }
251
252         public Task<TwitterUser> UsersShow(string screenName)
253         {
254             var endpoint = new Uri("users/show.json", UriKind.Relative);
255             var param = new Dictionary<string, string>
256             {
257                 ["screen_name"] = screenName,
258                 ["include_entities"] = "true",
259                 ["include_ext_alt_text"] = "true",
260             };
261
262             return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/users/show/:id");
263         }
264
265         public Task<LazyJson<TwitterUser>> UsersReportSpam(string screenName)
266         {
267             var endpoint = new Uri("users/report_spam.json", UriKind.Relative);
268             var param = new Dictionary<string, string>
269             {
270                 ["screen_name"] = screenName,
271             };
272
273             return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
274         }
275
276         public Task<LazyJson<TwitterStatus>> FavoritesCreate(long statusId)
277         {
278             var endpoint = new Uri("favorites/create.json", UriKind.Relative);
279             var param = new Dictionary<string, string>
280             {
281                 ["id"] = statusId.ToString(),
282             };
283
284             return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
285         }
286
287         public Task<LazyJson<TwitterStatus>> FavoritesDestroy(long statusId)
288         {
289             var endpoint = new Uri("favorites/destroy.json", UriKind.Relative);
290             var param = new Dictionary<string, string>
291             {
292                 ["id"] = statusId.ToString(),
293             };
294
295             return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
296         }
297
298         public Task<TwitterFriendship> FriendshipsShow(string sourceScreenName, string targetScreenName)
299         {
300             var endpoint = new Uri("friendships/show.json", UriKind.Relative);
301             var param = new Dictionary<string, string>
302             {
303                 ["source_screen_name"] = sourceScreenName,
304                 ["target_screen_name"] = targetScreenName,
305             };
306
307             return this.apiConnection.GetAsync<TwitterFriendship>(endpoint, param, "/friendships/show");
308         }
309
310         public Task<LazyJson<TwitterFriendship>> FriendshipsCreate(string screenName)
311         {
312             var endpoint = new Uri("friendships/create.json", UriKind.Relative);
313             var param = new Dictionary<string, string>
314             {
315                 ["screen_name"] = screenName,
316             };
317
318             return this.apiConnection.PostLazyAsync<TwitterFriendship>(endpoint, param);
319         }
320
321         public Task<LazyJson<TwitterFriendship>> FriendshipsDestroy(string screenName)
322         {
323             var endpoint = new Uri("friendships/destroy.json", UriKind.Relative);
324             var param = new Dictionary<string, string>
325             {
326                 ["screen_name"] = screenName,
327             };
328
329             return this.apiConnection.PostLazyAsync<TwitterFriendship>(endpoint, param);
330         }
331
332         public Task<long[]> NoRetweetIds(long? cursor = null)
333         {
334             var endpoint = new Uri("friendships/no_retweets/ids.json", UriKind.Relative);
335
336             return this.apiConnection.GetAsync<long[]>(endpoint, null, "/friendships/no_retweets/ids");
337         }
338
339         public Task<TwitterIds> FollowersIds(long? cursor = null)
340         {
341             var endpoint = new Uri("followers/ids.json", UriKind.Relative);
342             var param = new Dictionary<string, string>();
343
344             if (cursor != null)
345                 param["cursor"] = cursor.ToString();
346
347             return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/followers/ids");
348         }
349
350         public Task<TwitterIds> MutesUsersIds(long? cursor = null)
351         {
352             var endpoint = new Uri("mutes/users/ids.json", UriKind.Relative);
353             var param = new Dictionary<string, string>();
354
355             if (cursor != null)
356                 param["cursor"] = cursor.ToString();
357
358             return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/mutes/users/ids");
359         }
360
361         public Task<TwitterIds> BlocksIds(long? cursor = null)
362         {
363             var endpoint = new Uri("blocks/ids.json", UriKind.Relative);
364             var param = new Dictionary<string, string>();
365
366             if (cursor != null)
367                 param["cursor"] = cursor.ToString();
368
369             return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/blocks/ids");
370         }
371
372         public Task<LazyJson<TwitterUser>> BlocksCreate(string screenName)
373         {
374             var endpoint = new Uri("blocks/create.json", UriKind.Relative);
375             var param = new Dictionary<string, string>
376             {
377                 ["screen_name"] = screenName,
378             };
379
380             return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
381         }
382
383         public Task<LazyJson<TwitterUser>> BlocksDestroy(string screenName)
384         {
385             var endpoint = new Uri("blocks/destroy.json", UriKind.Relative);
386             var param = new Dictionary<string, string>
387             {
388                 ["screen_name"] = screenName,
389             };
390
391             return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
392         }
393
394         public Task<LazyJson<TwitterUser>> AccountUpdateProfile(string name, string url, string location, string description)
395         {
396             var endpoint = new Uri("account/update_profile.json", UriKind.Relative);
397             var param = new Dictionary<string, string>
398             {
399                 ["include_entities"] = "true",
400                 ["include_ext_alt_text"] = "true",
401             };
402
403             if (name != null)
404                 param["name"] = name;
405             if (url != null)
406                 param["url"] = url;
407             if (location != null)
408                 param["location"] = location;
409
410             if (description != null)
411             {
412                 // name, location, description に含まれる < > " の文字はTwitter側で除去されるが、
413                 // twitter.com の挙動では description でのみ &lt; 等の文字参照を使って表示することができる
414                 var escapedDescription = description.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;");
415                 param["description"] = escapedDescription;
416             }
417
418             return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
419         }
420
421         public Task<LazyJson<TwitterUser>> AccountUpdateProfileImage(IMediaItem image)
422         {
423             var endpoint = new Uri("account/update_profile_image.json", UriKind.Relative);
424             var param = new Dictionary<string, string>
425             {
426                 ["include_entities"] = "true",
427                 ["include_ext_alt_text"] = "true",
428             };
429             var paramMedia = new Dictionary<string, IMediaItem>
430             {
431                 ["image"] = image,
432             };
433
434             return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param, paramMedia);
435         }
436
437         public Task<TwitterConfiguration> Configuration()
438         {
439             var endpoint = new Uri("help/configuration.json", UriKind.Relative);
440
441             return this.apiConnection.GetAsync<TwitterConfiguration>(endpoint, null, "/help/configuration");
442         }
443
444         public Task<LazyJson<TwitterUploadMediaResult>> MediaUpload(IMediaItem media)
445         {
446             var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
447             var paramMedia = new Dictionary<string, IMediaItem>
448             {
449                 ["media"] = media,
450             };
451
452             return this.apiConnection.PostLazyAsync<TwitterUploadMediaResult>(endpoint, null, paramMedia);
453         }
454
455         public Task<Stream> UserStreams(string replies = null, string track = null)
456         {
457             var endpoint = new Uri("https://userstream.twitter.com/1.1/user.json");
458             var param = new Dictionary<string, string>();
459
460             if (replies != null)
461                 param["replies"] = replies;
462             if (track != null)
463                 param["track"] = track;
464
465             return this.apiConnection.GetStreamAsync(endpoint, param);
466         }
467
468         public OAuthEchoHandler CreateOAuthEchoHandler(Uri authServiceProvider, Uri realm = null)
469         {
470             return ((TwitterApiConnection)this.apiConnection).CreateOAuthEchoHandler(authServiceProvider, realm);
471         }
472
473         public void Dispose()
474         {
475             this.apiConnection?.Dispose();
476         }
477     }
478 }