1 // OpenTween - Client of Twitter
2 // Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
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)
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
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.
23 using System.Collections.Generic;
27 using System.Threading;
28 using System.Threading.Tasks;
29 using OpenTween.Api.DataModel;
30 using OpenTween.Connection;
32 namespace OpenTween.Api
34 public sealed class TwitterApi : IDisposable
36 public long CurrentUserId { get; private set; }
37 public string CurrentScreenName { get; private set; }
39 public IApiConnection Connection => this.apiConnection;
41 internal IApiConnection apiConnection;
43 public void Initialize(string accessToken, string accessSecret, long userId, string screenName)
45 var newInstance = new TwitterApiConnection(accessToken, accessSecret);
46 var oldInstance = Interlocked.Exchange(ref this.apiConnection, newInstance);
47 oldInstance?.Dispose();
49 this.CurrentUserId = userId;
50 this.CurrentScreenName = screenName;
53 public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, long? maxId = null, long? sinceId = null)
55 var endpoint = new Uri("statuses/home_timeline.json", UriKind.Relative);
56 var param = new Dictionary<string, string>
58 ["include_entities"] = "true",
59 ["include_ext_alt_text"] = "true",
60 ["tweet_mode"] = "extended",
64 param["count"] = count.ToString();
66 param["max_id"] = maxId.ToString();
68 param["since_id"] = sinceId.ToString();
70 return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/home_timeline");
73 public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, long? maxId = null, long? sinceId = null)
75 var endpoint = new Uri("statuses/mentions_timeline.json", UriKind.Relative);
76 var param = new Dictionary<string, string>
78 ["include_entities"] = "true",
79 ["include_ext_alt_text"] = "true",
80 ["tweet_mode"] = "extended",
84 param["count"] = count.ToString();
86 param["max_id"] = maxId.ToString();
88 param["since_id"] = sinceId.ToString();
90 return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/mentions_timeline");
93 public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, long? maxId = null, long? sinceId = null)
95 var endpoint = new Uri("statuses/user_timeline.json", UriKind.Relative);
96 var param = new Dictionary<string, string>
98 ["screen_name"] = screenName,
99 ["include_rts"] = "true",
100 ["include_entities"] = "true",
101 ["include_ext_alt_text"] = "true",
102 ["tweet_mode"] = "extended",
106 param["count"] = count.ToString();
108 param["max_id"] = maxId.ToString();
110 param["since_id"] = sinceId.ToString();
112 return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/user_timeline");
115 public Task<TwitterStatus> StatusesShow(long statusId)
117 var endpoint = new Uri("statuses/show.json", UriKind.Relative);
118 var param = new Dictionary<string, string>
120 ["id"] = statusId.ToString(),
121 ["include_entities"] = "true",
122 ["include_ext_alt_text"] = "true",
123 ["tweet_mode"] = "extended",
126 return this.apiConnection.GetAsync<TwitterStatus>(endpoint, param, "/statuses/show/:id");
129 public Task<LazyJson<TwitterStatus>> StatusesUpdate(string status, long? replyToId, IReadOnlyList<long> mediaIds,
130 bool? autoPopulateReplyMetadata = null, IReadOnlyList<long> excludeReplyUserIds = null, string attachmentUrl = null)
132 var endpoint = new Uri("statuses/update.json", UriKind.Relative);
133 var param = new Dictionary<string, string>
136 ["include_entities"] = "true",
137 ["include_ext_alt_text"] = "true",
138 ["tweet_mode"] = "extended",
141 if (replyToId != null)
142 param["in_reply_to_status_id"] = replyToId.ToString();
143 if (mediaIds != null && mediaIds.Count > 0)
144 param.Add("media_ids", string.Join(",", mediaIds));
145 if (autoPopulateReplyMetadata != null)
146 param["auto_populate_reply_metadata"] = autoPopulateReplyMetadata.Value ? "true" : "false";
147 if (excludeReplyUserIds != null && excludeReplyUserIds.Count > 0)
148 param["exclude_reply_user_ids"] = string.Join(",", excludeReplyUserIds);
149 if (attachmentUrl != null)
150 param["attachment_url"] = attachmentUrl;
152 return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
155 public Task<LazyJson<TwitterStatus>> StatusesDestroy(long statusId)
157 var endpoint = new Uri("statuses/destroy.json", UriKind.Relative);
158 var param = new Dictionary<string, string>
160 ["id"] = statusId.ToString(),
163 return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
166 public Task<LazyJson<TwitterStatus>> StatusesRetweet(long statusId)
168 var endpoint = new Uri("statuses/retweet.json", UriKind.Relative);
169 var param = new Dictionary<string, string>
171 ["id"] = statusId.ToString(),
172 ["include_entities"] = "true",
173 ["include_ext_alt_text"] = "true",
174 ["tweet_mode"] = "extended",
177 return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
180 public Task<TwitterSearchResult> SearchTweets(string query, string lang = null, int? count = null, long? maxId = null, long? sinceId = null)
182 var endpoint = new Uri("search/tweets.json", UriKind.Relative);
183 var param = new Dictionary<string, string>
186 ["result_type"] = "recent",
187 ["include_entities"] = "true",
188 ["include_ext_alt_text"] = "true",
189 ["tweet_mode"] = "extended",
193 param["lang"] = lang;
195 param["count"] = count.ToString();
197 param["max_id"] = maxId.ToString();
199 param["since_id"] = sinceId.ToString();
201 return this.apiConnection.GetAsync<TwitterSearchResult>(endpoint, param, "/search/tweets");
204 public Task<TwitterLists> ListsOwnerships(string screenName, long? cursor = null, int? count = null)
206 var endpoint = new Uri("lists/ownerships.json", UriKind.Relative);
207 var param = new Dictionary<string, string>
209 ["screen_name"] = screenName,
213 param["cursor"] = cursor.ToString();
215 param["count"] = count.ToString();
217 return this.apiConnection.GetAsync<TwitterLists>(endpoint, param, "/lists/ownerships");
220 public Task<TwitterLists> ListsSubscriptions(string screenName, long? cursor = null, int? count = null)
222 var endpoint = new Uri("lists/subscriptions.json", UriKind.Relative);
223 var param = new Dictionary<string, string>
225 ["screen_name"] = screenName,
229 param["cursor"] = cursor.ToString();
231 param["count"] = count.ToString();
233 return this.apiConnection.GetAsync<TwitterLists>(endpoint, param, "/lists/subscriptions");
236 public Task<TwitterLists> ListsMemberships(string screenName, long? cursor = null, int? count = null, bool? filterToOwnedLists = null)
238 var endpoint = new Uri("lists/memberships.json", UriKind.Relative);
239 var param = new Dictionary<string, string>
241 ["screen_name"] = screenName,
245 param["cursor"] = cursor.ToString();
247 param["count"] = count.ToString();
248 if (filterToOwnedLists != null)
249 param["filter_to_owned_lists"] = filterToOwnedLists.Value ? "true" : "false";
251 return this.apiConnection.GetAsync<TwitterLists>(endpoint, param, "/lists/memberships");
254 public Task<LazyJson<TwitterList>> ListsCreate(string name, string description = null, bool? @private = null)
256 var endpoint = new Uri("lists/create.json", UriKind.Relative);
257 var param = new Dictionary<string, string>
262 if (description != null)
263 param["description"] = description;
264 if (@private != null)
265 param["mode"] = @private.Value ? "private" : "public";
267 return this.apiConnection.PostLazyAsync<TwitterList>(endpoint, param);
270 public Task<LazyJson<TwitterList>> ListsUpdate(long listId, string name = null, string description = null, bool? @private = null)
272 var endpoint = new Uri("lists/update.json", UriKind.Relative);
273 var param = new Dictionary<string, string>
275 ["list_id"] = listId.ToString(),
279 param["name"] = name;
280 if (description != null)
281 param["description"] = description;
282 if (@private != null)
283 param["mode"] = @private.Value ? "private" : "public";
285 return this.apiConnection.PostLazyAsync<TwitterList>(endpoint, param);
288 public Task<LazyJson<TwitterList>> ListsDestroy(long listId)
290 var endpoint = new Uri("lists/destroy.json", UriKind.Relative);
291 var param = new Dictionary<string, string>
293 ["list_id"] = listId.ToString(),
296 return this.apiConnection.PostLazyAsync<TwitterList>(endpoint, param);
299 public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, long? maxId = null, long? sinceId = null, bool? includeRTs = null)
301 var endpoint = new Uri("lists/statuses.json", UriKind.Relative);
302 var param = new Dictionary<string, string>
304 ["list_id"] = listId.ToString(),
305 ["include_entities"] = "true",
306 ["include_ext_alt_text"] = "true",
307 ["tweet_mode"] = "extended",
311 param["count"] = count.ToString();
313 param["max_id"] = maxId.ToString();
315 param["since_id"] = sinceId.ToString();
316 if (includeRTs != null)
317 param["include_rts"] = includeRTs.Value ? "true" : "false";
319 return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/lists/statuses");
322 public Task<TwitterUsers> ListsMembers(long listId, long? cursor = null)
324 var endpoint = new Uri("lists/members.json", UriKind.Relative);
325 var param = new Dictionary<string, string>
327 ["list_id"] = listId.ToString(),
328 ["include_entities"] = "true",
329 ["include_ext_alt_text"] = "true",
330 ["tweet_mode"] = "extended",
334 param["cursor"] = cursor.ToString();
336 return this.apiConnection.GetAsync<TwitterUsers>(endpoint, param, "/lists/members");
339 public Task<TwitterUser> ListsMembersShow(long listId, string screenName)
341 var endpoint = new Uri("lists/members/show.json", UriKind.Relative);
342 var param = new Dictionary<string, string>
344 ["list_id"] = listId.ToString(),
345 ["screen_name"] = screenName,
346 ["include_entities"] = "true",
347 ["include_ext_alt_text"] = "true",
348 ["tweet_mode"] = "extended",
351 return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/lists/members/show");
354 public Task<LazyJson<TwitterUser>> ListsMembersCreate(long listId, string screenName)
356 var endpoint = new Uri("lists/members/create.json", UriKind.Relative);
357 var param = new Dictionary<string, string>
359 ["list_id"] = listId.ToString(),
360 ["screen_name"] = screenName,
361 ["include_entities"] = "true",
362 ["include_ext_alt_text"] = "true",
363 ["tweet_mode"] = "extended",
366 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
369 public Task<LazyJson<TwitterUser>> ListsMembersDestroy(long listId, string screenName)
371 var endpoint = new Uri("lists/members/destroy.json", UriKind.Relative);
372 var param = new Dictionary<string, string>
374 ["list_id"] = listId.ToString(),
375 ["screen_name"] = screenName,
376 ["include_entities"] = "true",
377 ["include_ext_alt_text"] = "true",
378 ["tweet_mode"] = "extended",
381 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
384 public Task<TwitterMessageEventList> DirectMessagesEventsList(int? count = null, string cursor = null)
386 var endpoint = new Uri("direct_messages/events/list.json", UriKind.Relative);
387 var param = new Dictionary<string, string>();
390 param["count"] = count.ToString();
392 param["cursor"] = cursor;
394 return this.apiConnection.GetAsync<TwitterMessageEventList>(endpoint, param, "/direct_messages/events/list");
397 public Task DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
399 var endpoint = new Uri("direct_messages/events/new.json", UriKind.Relative);
404 attachment = "," + $@"
408 ""id"": ""{JsonUtils.EscapeJsonString(mediaId.ToString())}""
415 ""type"": ""message_create"",
416 ""message_create"": {{
418 ""recipient_id"": ""{JsonUtils.EscapeJsonString(recipientId.ToString())}""
421 ""text"": ""{JsonUtils.EscapeJsonString(text)}""{attachment}
427 return this.apiConnection.PostJsonAsync(endpoint, json);
430 public Task DirectMessagesEventsDestroy(string eventId)
432 var endpoint = new Uri("direct_messages/events/destroy.json", UriKind.Relative);
433 var param = new Dictionary<string, string>
435 ["id"] = eventId.ToString(),
438 // なぜか application/x-www-form-urlencoded でパラメーターを送ると Bad Request になる謎仕様
439 endpoint = new Uri(endpoint.OriginalString + "?" + MyCommon.BuildQueryString(param), UriKind.Relative);
441 return this.apiConnection.DeleteAsync(endpoint);
444 public Task<TwitterUser> UsersShow(string screenName)
446 var endpoint = new Uri("users/show.json", UriKind.Relative);
447 var param = new Dictionary<string, string>
449 ["screen_name"] = screenName,
450 ["include_entities"] = "true",
451 ["include_ext_alt_text"] = "true",
452 ["tweet_mode"] = "extended",
455 return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/users/show/:id");
458 public Task<TwitterUser[]> UsersLookup(IReadOnlyList<string> userIds)
460 var endpoint = new Uri("users/lookup.json", UriKind.Relative);
461 var param = new Dictionary<string, string>
463 ["user_id"] = string.Join(",", userIds),
464 ["include_entities"] = "true",
465 ["include_ext_alt_text"] = "true",
466 ["tweet_mode"] = "extended",
469 return this.apiConnection.GetAsync<TwitterUser[]>(endpoint, param, "/users/lookup");
472 public Task<LazyJson<TwitterUser>> UsersReportSpam(string screenName)
474 var endpoint = new Uri("users/report_spam.json", UriKind.Relative);
475 var param = new Dictionary<string, string>
477 ["screen_name"] = screenName,
478 ["tweet_mode"] = "extended",
481 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
484 public Task<TwitterStatus[]> FavoritesList(int? count = null, long? maxId = null, long? sinceId = null)
486 var endpoint = new Uri("favorites/list.json", UriKind.Relative);
487 var param = new Dictionary<string, string>
489 ["include_entities"] = "true",
490 ["include_ext_alt_text"] = "true",
491 ["tweet_mode"] = "extended",
495 param["count"] = count.ToString();
497 param["max_id"] = maxId.ToString();
499 param["since_id"] = sinceId.ToString();
501 return this.apiConnection.GetAsync<TwitterStatus[]>(endpoint, param, "/favorites/list");
504 public Task<LazyJson<TwitterStatus>> FavoritesCreate(long statusId)
506 var endpoint = new Uri("favorites/create.json", UriKind.Relative);
507 var param = new Dictionary<string, string>
509 ["id"] = statusId.ToString(),
510 ["tweet_mode"] = "extended",
513 return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
516 public Task<LazyJson<TwitterStatus>> FavoritesDestroy(long statusId)
518 var endpoint = new Uri("favorites/destroy.json", UriKind.Relative);
519 var param = new Dictionary<string, string>
521 ["id"] = statusId.ToString(),
522 ["tweet_mode"] = "extended",
525 return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
528 public Task<TwitterFriendship> FriendshipsShow(string sourceScreenName, string targetScreenName)
530 var endpoint = new Uri("friendships/show.json", UriKind.Relative);
531 var param = new Dictionary<string, string>
533 ["source_screen_name"] = sourceScreenName,
534 ["target_screen_name"] = targetScreenName,
537 return this.apiConnection.GetAsync<TwitterFriendship>(endpoint, param, "/friendships/show");
540 public Task<LazyJson<TwitterFriendship>> FriendshipsCreate(string screenName)
542 var endpoint = new Uri("friendships/create.json", UriKind.Relative);
543 var param = new Dictionary<string, string>
545 ["screen_name"] = screenName,
548 return this.apiConnection.PostLazyAsync<TwitterFriendship>(endpoint, param);
551 public Task<LazyJson<TwitterFriendship>> FriendshipsDestroy(string screenName)
553 var endpoint = new Uri("friendships/destroy.json", UriKind.Relative);
554 var param = new Dictionary<string, string>
556 ["screen_name"] = screenName,
559 return this.apiConnection.PostLazyAsync<TwitterFriendship>(endpoint, param);
562 public Task<long[]> NoRetweetIds()
564 var endpoint = new Uri("friendships/no_retweets/ids.json", UriKind.Relative);
566 return this.apiConnection.GetAsync<long[]>(endpoint, null, "/friendships/no_retweets/ids");
569 public Task<TwitterIds> FollowersIds(long? cursor = null)
571 var endpoint = new Uri("followers/ids.json", UriKind.Relative);
572 var param = new Dictionary<string, string>();
575 param["cursor"] = cursor.ToString();
577 return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/followers/ids");
580 public Task<TwitterIds> MutesUsersIds(long? cursor = null)
582 var endpoint = new Uri("mutes/users/ids.json", UriKind.Relative);
583 var param = new Dictionary<string, string>();
586 param["cursor"] = cursor.ToString();
588 return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/mutes/users/ids");
591 public Task<TwitterIds> BlocksIds(long? cursor = null)
593 var endpoint = new Uri("blocks/ids.json", UriKind.Relative);
594 var param = new Dictionary<string, string>();
597 param["cursor"] = cursor.ToString();
599 return this.apiConnection.GetAsync<TwitterIds>(endpoint, param, "/blocks/ids");
602 public Task<LazyJson<TwitterUser>> BlocksCreate(string screenName)
604 var endpoint = new Uri("blocks/create.json", UriKind.Relative);
605 var param = new Dictionary<string, string>
607 ["screen_name"] = screenName,
608 ["tweet_mode"] = "extended",
611 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
614 public Task<LazyJson<TwitterUser>> BlocksDestroy(string screenName)
616 var endpoint = new Uri("blocks/destroy.json", UriKind.Relative);
617 var param = new Dictionary<string, string>
619 ["screen_name"] = screenName,
620 ["tweet_mode"] = "extended",
623 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
626 public async Task<TwitterUser> AccountVerifyCredentials()
628 var endpoint = new Uri("account/verify_credentials.json", UriKind.Relative);
629 var param = new Dictionary<string, string>
631 ["include_entities"] = "true",
632 ["include_ext_alt_text"] = "true",
633 ["tweet_mode"] = "extended",
636 var user = await this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/account/verify_credentials")
637 .ConfigureAwait(false);
639 this.CurrentUserId = user.Id;
640 this.CurrentScreenName = user.ScreenName;
645 public Task<LazyJson<TwitterUser>> AccountUpdateProfile(string name, string url, string location, string description)
647 var endpoint = new Uri("account/update_profile.json", UriKind.Relative);
648 var param = new Dictionary<string, string>
650 ["include_entities"] = "true",
651 ["include_ext_alt_text"] = "true",
652 ["tweet_mode"] = "extended",
656 param["name"] = name;
659 if (location != null)
660 param["location"] = location;
662 if (description != null)
664 // name, location, description に含まれる < > " の文字はTwitter側で除去されるが、
665 // twitter.com の挙動では description でのみ < 等の文字参照を使って表示することができる
666 var escapedDescription = description.Replace("<", "<").Replace(">", ">").Replace("\"", """);
667 param["description"] = escapedDescription;
670 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
673 public Task<LazyJson<TwitterUser>> AccountUpdateProfileImage(IMediaItem image)
675 var endpoint = new Uri("account/update_profile_image.json", UriKind.Relative);
676 var param = new Dictionary<string, string>
678 ["include_entities"] = "true",
679 ["include_ext_alt_text"] = "true",
680 ["tweet_mode"] = "extended",
682 var paramMedia = new Dictionary<string, IMediaItem>
687 return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param, paramMedia);
690 public Task<TwitterRateLimits> ApplicationRateLimitStatus()
692 var endpoint = new Uri("application/rate_limit_status.json", UriKind.Relative);
694 return this.apiConnection.GetAsync<TwitterRateLimits>(endpoint, null, "/application/rate_limit_status");
697 public Task<TwitterConfiguration> Configuration()
699 var endpoint = new Uri("help/configuration.json", UriKind.Relative);
701 return this.apiConnection.GetAsync<TwitterConfiguration>(endpoint, null, "/help/configuration");
704 public Task<LazyJson<TwitterUploadMediaInit>> MediaUploadInit(long totalBytes, string mediaType, string mediaCategory = null)
706 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
707 var param = new Dictionary<string, string>
709 ["command"] = "INIT",
710 ["total_bytes"] = totalBytes.ToString(),
711 ["media_type"] = mediaType,
714 if (mediaCategory != null)
715 param["media_category"] = mediaCategory;
717 return this.apiConnection.PostLazyAsync<TwitterUploadMediaInit>(endpoint, param);
720 public Task MediaUploadAppend(long mediaId, int segmentIndex, IMediaItem media)
722 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
723 var param = new Dictionary<string, string>
725 ["command"] = "APPEND",
726 ["media_id"] = mediaId.ToString(),
727 ["segment_index"] = segmentIndex.ToString(),
729 var paramMedia = new Dictionary<string, IMediaItem>
734 return this.apiConnection.PostAsync(endpoint, param, paramMedia);
737 public Task<LazyJson<TwitterUploadMediaResult>> MediaUploadFinalize(long mediaId)
739 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
740 var param = new Dictionary<string, string>
742 ["command"] = "FINALIZE",
743 ["media_id"] = mediaId.ToString(),
746 return this.apiConnection.PostLazyAsync<TwitterUploadMediaResult>(endpoint, param);
749 public Task<TwitterUploadMediaResult> MediaUploadStatus(long mediaId)
751 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
752 var param = new Dictionary<string, string>
754 ["command"] = "STATUS",
755 ["media_id"] = mediaId.ToString(),
758 return this.apiConnection.GetAsync<TwitterUploadMediaResult>(endpoint, param, endpointName: null);
761 public Task MediaMetadataCreate(long mediaId, string altText)
763 var endpoint = new Uri("https://upload.twitter.com/1.1/media/metadata/create.json");
765 var escapedAltText = JsonUtils.EscapeJsonString(altText);
766 var json = $@"{{""media_id"": ""{mediaId}"", ""alt_text"": {{""text"": ""{escapedAltText}""}}}}";
768 return this.apiConnection.PostJsonAsync(endpoint, json);
771 public TwitterStreamObservable UserStreams(string replies = null, string track = null)
773 var endpoint = new Uri("https://userstream.twitter.com/1.1/user.json");
774 var param = new Dictionary<string, string>();
776 if (!string.IsNullOrEmpty(replies))
777 param["replies"] = replies;
778 if (!string.IsNullOrEmpty(track))
779 param["track"] = track;
781 Task<Stream> openStream()
782 => this.apiConnection.GetStreamingStreamAsync(endpoint, param);
784 return new TwitterStreamObservable(openStream);
787 public OAuthEchoHandler CreateOAuthEchoHandler(Uri authServiceProvider, Uri realm = null)
788 => ((TwitterApiConnection)this.apiConnection).CreateOAuthEchoHandler(authServiceProvider, realm);
790 public void Dispose()
791 => this.apiConnection?.Dispose();