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.
25 using System.Collections.Generic;
29 using System.Threading;
30 using System.Threading.Tasks;
31 using OpenTween.Api.DataModel;
32 using OpenTween.Connection;
34 namespace OpenTween.Api
36 public sealed class TwitterApi : IDisposable
38 public long CurrentUserId { get; private set; }
40 public string CurrentScreenName { get; private set; } = "";
42 public IApiConnection Connection => this.ApiConnection ?? throw new InvalidOperationException();
44 internal IApiConnection? ApiConnection;
46 private TwitterAppToken appToken = TwitterAppToken.GetDefault();
52 public TwitterApi(ApiKey consumerKey, ApiKey consumerSecret)
56 AuthType = APIAuthType.OAuth1,
57 OAuth1ConsumerKey = consumerKey,
58 OAuth1ConsumerSecret = consumerSecret,
62 public void Initialize(string accessToken, string accessSecret, long userId, string screenName)
63 => this.Initialize(this.appToken, accessToken, accessSecret, userId, screenName);
65 public void Initialize(TwitterAppToken appToken, string accessToken, string accessSecret, long userId, string screenName)
67 this.appToken = appToken;
69 var newInstance = new TwitterApiConnection(this.appToken, accessToken, accessSecret);
70 var oldInstance = Interlocked.Exchange(ref this.ApiConnection, newInstance);
71 oldInstance?.Dispose();
73 this.CurrentUserId = userId;
74 this.CurrentScreenName = screenName;
77 public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, long? maxId = null, long? sinceId = null)
79 var endpoint = new Uri("statuses/home_timeline.json", UriKind.Relative);
80 var param = new Dictionary<string, string>
82 ["include_entities"] = "true",
83 ["include_ext_alt_text"] = "true",
84 ["tweet_mode"] = "extended",
88 param["count"] = count.ToString();
90 param["max_id"] = maxId.ToString();
92 param["since_id"] = sinceId.ToString();
94 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/home_timeline");
97 public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, long? maxId = null, long? sinceId = null)
99 var endpoint = new Uri("statuses/mentions_timeline.json", UriKind.Relative);
100 var param = new Dictionary<string, string>
102 ["include_entities"] = "true",
103 ["include_ext_alt_text"] = "true",
104 ["tweet_mode"] = "extended",
108 param["count"] = count.ToString();
110 param["max_id"] = maxId.ToString();
112 param["since_id"] = sinceId.ToString();
114 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/mentions_timeline");
117 public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, long? maxId = null, long? sinceId = null)
119 var endpoint = new Uri("statuses/user_timeline.json", UriKind.Relative);
120 var param = new Dictionary<string, string>
122 ["screen_name"] = screenName,
123 ["include_rts"] = "true",
124 ["include_entities"] = "true",
125 ["include_ext_alt_text"] = "true",
126 ["tweet_mode"] = "extended",
130 param["count"] = count.ToString();
132 param["max_id"] = maxId.ToString();
134 param["since_id"] = sinceId.ToString();
136 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/user_timeline");
139 public Task<TwitterStatus> StatusesShow(long statusId)
141 var endpoint = new Uri("statuses/show.json", UriKind.Relative);
142 var param = new Dictionary<string, string>
144 ["id"] = statusId.ToString(),
145 ["include_entities"] = "true",
146 ["include_ext_alt_text"] = "true",
147 ["tweet_mode"] = "extended",
150 return this.Connection.GetAsync<TwitterStatus>(endpoint, param, "/statuses/show/:id");
153 public Task<TwitterStatus[]> StatusesLookup(IReadOnlyList<string> statusIds)
155 var endpoint = new Uri("statuses/lookup.json", UriKind.Relative);
156 var param = new Dictionary<string, string>
158 ["id"] = string.Join(",", statusIds),
159 ["include_entities"] = "true",
160 ["include_ext_alt_text"] = "true",
161 ["tweet_mode"] = "extended",
164 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/lookup");
167 public Task<LazyJson<TwitterStatus>> StatusesUpdate(
170 IReadOnlyList<long>? mediaIds,
171 bool? autoPopulateReplyMetadata = null,
172 IReadOnlyList<long>? excludeReplyUserIds = null,
173 string? attachmentUrl = null)
175 var endpoint = new Uri("statuses/update.json", UriKind.Relative);
176 var param = new Dictionary<string, string>
179 ["include_entities"] = "true",
180 ["include_ext_alt_text"] = "true",
181 ["tweet_mode"] = "extended",
184 if (replyToId != null)
185 param["in_reply_to_status_id"] = replyToId.ToString();
186 if (mediaIds != null && mediaIds.Count > 0)
187 param.Add("media_ids", string.Join(",", mediaIds));
188 if (autoPopulateReplyMetadata != null)
189 param["auto_populate_reply_metadata"] = autoPopulateReplyMetadata.Value ? "true" : "false";
190 if (excludeReplyUserIds != null && excludeReplyUserIds.Count > 0)
191 param["exclude_reply_user_ids"] = string.Join(",", excludeReplyUserIds);
192 if (attachmentUrl != null)
193 param["attachment_url"] = attachmentUrl;
195 return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
198 public Task<LazyJson<TwitterStatus>> StatusesDestroy(long statusId)
200 var endpoint = new Uri("statuses/destroy.json", UriKind.Relative);
201 var param = new Dictionary<string, string>
203 ["id"] = statusId.ToString(),
206 return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
209 public Task<LazyJson<TwitterStatus>> StatusesRetweet(long statusId)
211 var endpoint = new Uri("statuses/retweet.json", UriKind.Relative);
212 var param = new Dictionary<string, string>
214 ["id"] = statusId.ToString(),
215 ["include_entities"] = "true",
216 ["include_ext_alt_text"] = "true",
217 ["tweet_mode"] = "extended",
220 return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
223 public Task<TwitterSearchResult> SearchTweets(string query, string? lang = null, int? count = null, long? maxId = null, long? sinceId = null)
225 var endpoint = new Uri("search/tweets.json", UriKind.Relative);
226 var param = new Dictionary<string, string>
229 ["result_type"] = "recent",
230 ["include_entities"] = "true",
231 ["include_ext_alt_text"] = "true",
232 ["tweet_mode"] = "extended",
236 param["lang"] = lang;
238 param["count"] = count.ToString();
240 param["max_id"] = maxId.ToString();
242 param["since_id"] = sinceId.ToString();
244 return this.Connection.GetAsync<TwitterSearchResult>(endpoint, param, "/search/tweets");
247 public Task<TwitterLists> ListsOwnerships(string screenName, long? cursor = null, int? count = null)
249 var endpoint = new Uri("lists/ownerships.json", UriKind.Relative);
250 var param = new Dictionary<string, string>
252 ["screen_name"] = screenName,
256 param["cursor"] = cursor.ToString();
258 param["count"] = count.ToString();
260 return this.Connection.GetAsync<TwitterLists>(endpoint, param, "/lists/ownerships");
263 public Task<TwitterLists> ListsSubscriptions(string screenName, long? cursor = null, int? count = null)
265 var endpoint = new Uri("lists/subscriptions.json", UriKind.Relative);
266 var param = new Dictionary<string, string>
268 ["screen_name"] = screenName,
272 param["cursor"] = cursor.ToString();
274 param["count"] = count.ToString();
276 return this.Connection.GetAsync<TwitterLists>(endpoint, param, "/lists/subscriptions");
279 public Task<TwitterLists> ListsMemberships(string screenName, long? cursor = null, int? count = null, bool? filterToOwnedLists = null)
281 var endpoint = new Uri("lists/memberships.json", UriKind.Relative);
282 var param = new Dictionary<string, string>
284 ["screen_name"] = screenName,
288 param["cursor"] = cursor.ToString();
290 param["count"] = count.ToString();
291 if (filterToOwnedLists != null)
292 param["filter_to_owned_lists"] = filterToOwnedLists.Value ? "true" : "false";
294 return this.Connection.GetAsync<TwitterLists>(endpoint, param, "/lists/memberships");
297 public Task<LazyJson<TwitterList>> ListsCreate(string name, string? description = null, bool? @private = null)
299 var endpoint = new Uri("lists/create.json", UriKind.Relative);
300 var param = new Dictionary<string, string>
305 if (description != null)
306 param["description"] = description;
307 if (@private != null)
308 param["mode"] = @private.Value ? "private" : "public";
310 return this.Connection.PostLazyAsync<TwitterList>(endpoint, param);
313 public Task<LazyJson<TwitterList>> ListsUpdate(long listId, string? name = null, string? description = null, bool? @private = null)
315 var endpoint = new Uri("lists/update.json", UriKind.Relative);
316 var param = new Dictionary<string, string>
318 ["list_id"] = listId.ToString(),
322 param["name"] = name;
323 if (description != null)
324 param["description"] = description;
325 if (@private != null)
326 param["mode"] = @private.Value ? "private" : "public";
328 return this.Connection.PostLazyAsync<TwitterList>(endpoint, param);
331 public Task<LazyJson<TwitterList>> ListsDestroy(long listId)
333 var endpoint = new Uri("lists/destroy.json", UriKind.Relative);
334 var param = new Dictionary<string, string>
336 ["list_id"] = listId.ToString(),
339 return this.Connection.PostLazyAsync<TwitterList>(endpoint, param);
342 public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, long? maxId = null, long? sinceId = null, bool? includeRTs = null)
344 var endpoint = new Uri("lists/statuses.json", UriKind.Relative);
345 var param = new Dictionary<string, string>
347 ["list_id"] = listId.ToString(),
348 ["include_entities"] = "true",
349 ["include_ext_alt_text"] = "true",
350 ["tweet_mode"] = "extended",
354 param["count"] = count.ToString();
356 param["max_id"] = maxId.ToString();
358 param["since_id"] = sinceId.ToString();
359 if (includeRTs != null)
360 param["include_rts"] = includeRTs.Value ? "true" : "false";
362 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/lists/statuses");
365 public Task<TwitterUsers> ListsMembers(long listId, long? cursor = null)
367 var endpoint = new Uri("lists/members.json", UriKind.Relative);
368 var param = new Dictionary<string, string>
370 ["list_id"] = listId.ToString(),
371 ["include_entities"] = "true",
372 ["include_ext_alt_text"] = "true",
373 ["tweet_mode"] = "extended",
377 param["cursor"] = cursor.ToString();
379 return this.Connection.GetAsync<TwitterUsers>(endpoint, param, "/lists/members");
382 public Task<TwitterUser> ListsMembersShow(long listId, string screenName)
384 var endpoint = new Uri("lists/members/show.json", UriKind.Relative);
385 var param = new Dictionary<string, string>
387 ["list_id"] = listId.ToString(),
388 ["screen_name"] = screenName,
389 ["include_entities"] = "true",
390 ["include_ext_alt_text"] = "true",
391 ["tweet_mode"] = "extended",
394 return this.Connection.GetAsync<TwitterUser>(endpoint, param, "/lists/members/show");
397 public Task<LazyJson<TwitterUser>> ListsMembersCreate(long listId, string screenName)
399 var endpoint = new Uri("lists/members/create.json", UriKind.Relative);
400 var param = new Dictionary<string, string>
402 ["list_id"] = listId.ToString(),
403 ["screen_name"] = screenName,
404 ["include_entities"] = "true",
405 ["include_ext_alt_text"] = "true",
406 ["tweet_mode"] = "extended",
409 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
412 public Task<LazyJson<TwitterUser>> ListsMembersDestroy(long listId, string screenName)
414 var endpoint = new Uri("lists/members/destroy.json", UriKind.Relative);
415 var param = new Dictionary<string, string>
417 ["list_id"] = listId.ToString(),
418 ["screen_name"] = screenName,
419 ["include_entities"] = "true",
420 ["include_ext_alt_text"] = "true",
421 ["tweet_mode"] = "extended",
424 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
427 public Task<TwitterMessageEventList> DirectMessagesEventsList(int? count = null, string? cursor = null)
429 var endpoint = new Uri("direct_messages/events/list.json", UriKind.Relative);
430 var param = new Dictionary<string, string>();
433 param["count"] = count.ToString();
435 param["cursor"] = cursor;
437 return this.Connection.GetAsync<TwitterMessageEventList>(endpoint, param, "/direct_messages/events/list");
440 public Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
442 var endpoint = new Uri("direct_messages/events/new.json", UriKind.Relative);
447 attachment = "," + $@"
451 ""id"": ""{JsonUtils.EscapeJsonString(mediaId.ToString())}""
458 ""type"": ""message_create"",
459 ""message_create"": {{
461 ""recipient_id"": ""{JsonUtils.EscapeJsonString(recipientId.ToString())}""
464 ""text"": ""{JsonUtils.EscapeJsonString(text)}""{attachment}
470 return this.Connection.PostJsonAsync<TwitterMessageEventSingle>(endpoint, json);
473 public Task DirectMessagesEventsDestroy(string eventId)
475 var endpoint = new Uri("direct_messages/events/destroy.json", UriKind.Relative);
476 var param = new Dictionary<string, string>
478 ["id"] = eventId.ToString(),
481 // なぜか application/x-www-form-urlencoded でパラメーターを送ると Bad Request になる謎仕様
482 endpoint = new Uri(endpoint.OriginalString + "?" + MyCommon.BuildQueryString(param), UriKind.Relative);
484 return this.Connection.DeleteAsync(endpoint);
487 public Task<TwitterUser> UsersShow(string screenName)
489 var endpoint = new Uri("users/show.json", UriKind.Relative);
490 var param = new Dictionary<string, string>
492 ["screen_name"] = screenName,
493 ["include_entities"] = "true",
494 ["include_ext_alt_text"] = "true",
495 ["tweet_mode"] = "extended",
498 return this.Connection.GetAsync<TwitterUser>(endpoint, param, "/users/show/:id");
501 public Task<TwitterUser[]> UsersLookup(IReadOnlyList<string> userIds)
503 var endpoint = new Uri("users/lookup.json", UriKind.Relative);
504 var param = new Dictionary<string, string>
506 ["user_id"] = string.Join(",", userIds),
507 ["include_entities"] = "true",
508 ["include_ext_alt_text"] = "true",
509 ["tweet_mode"] = "extended",
512 return this.Connection.GetAsync<TwitterUser[]>(endpoint, param, "/users/lookup");
515 public Task<LazyJson<TwitterUser>> UsersReportSpam(string screenName)
517 var endpoint = new Uri("users/report_spam.json", UriKind.Relative);
518 var param = new Dictionary<string, string>
520 ["screen_name"] = screenName,
521 ["tweet_mode"] = "extended",
524 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
527 public Task<TwitterStatus[]> FavoritesList(int? count = null, long? maxId = null, long? sinceId = null)
529 var endpoint = new Uri("favorites/list.json", UriKind.Relative);
530 var param = new Dictionary<string, string>
532 ["include_entities"] = "true",
533 ["include_ext_alt_text"] = "true",
534 ["tweet_mode"] = "extended",
538 param["count"] = count.ToString();
540 param["max_id"] = maxId.ToString();
542 param["since_id"] = sinceId.ToString();
544 return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/favorites/list");
547 public Task<LazyJson<TwitterStatus>> FavoritesCreate(long statusId)
549 var endpoint = new Uri("favorites/create.json", UriKind.Relative);
550 var param = new Dictionary<string, string>
552 ["id"] = statusId.ToString(),
553 ["tweet_mode"] = "extended",
556 return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
559 public Task<LazyJson<TwitterStatus>> FavoritesDestroy(long statusId)
561 var endpoint = new Uri("favorites/destroy.json", UriKind.Relative);
562 var param = new Dictionary<string, string>
564 ["id"] = statusId.ToString(),
565 ["tweet_mode"] = "extended",
568 return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
571 public Task<TwitterFriendship> FriendshipsShow(string sourceScreenName, string targetScreenName)
573 var endpoint = new Uri("friendships/show.json", UriKind.Relative);
574 var param = new Dictionary<string, string>
576 ["source_screen_name"] = sourceScreenName,
577 ["target_screen_name"] = targetScreenName,
580 return this.Connection.GetAsync<TwitterFriendship>(endpoint, param, "/friendships/show");
583 public Task<LazyJson<TwitterFriendship>> FriendshipsCreate(string screenName)
585 var endpoint = new Uri("friendships/create.json", UriKind.Relative);
586 var param = new Dictionary<string, string>
588 ["screen_name"] = screenName,
591 return this.Connection.PostLazyAsync<TwitterFriendship>(endpoint, param);
594 public Task<LazyJson<TwitterFriendship>> FriendshipsDestroy(string screenName)
596 var endpoint = new Uri("friendships/destroy.json", UriKind.Relative);
597 var param = new Dictionary<string, string>
599 ["screen_name"] = screenName,
602 return this.Connection.PostLazyAsync<TwitterFriendship>(endpoint, param);
605 public Task<long[]> NoRetweetIds()
607 var endpoint = new Uri("friendships/no_retweets/ids.json", UriKind.Relative);
609 return this.Connection.GetAsync<long[]>(endpoint, null, "/friendships/no_retweets/ids");
612 public Task<TwitterIds> FollowersIds(long? cursor = null)
614 var endpoint = new Uri("followers/ids.json", UriKind.Relative);
615 var param = new Dictionary<string, string>();
618 param["cursor"] = cursor.ToString();
620 return this.Connection.GetAsync<TwitterIds>(endpoint, param, "/followers/ids");
623 public Task<TwitterIds> MutesUsersIds(long? cursor = null)
625 var endpoint = new Uri("mutes/users/ids.json", UriKind.Relative);
626 var param = new Dictionary<string, string>();
629 param["cursor"] = cursor.ToString();
631 return this.Connection.GetAsync<TwitterIds>(endpoint, param, "/mutes/users/ids");
634 public Task<TwitterIds> BlocksIds(long? cursor = null)
636 var endpoint = new Uri("blocks/ids.json", UriKind.Relative);
637 var param = new Dictionary<string, string>();
640 param["cursor"] = cursor.ToString();
642 return this.Connection.GetAsync<TwitterIds>(endpoint, param, "/blocks/ids");
645 public Task<LazyJson<TwitterUser>> BlocksCreate(string screenName)
647 var endpoint = new Uri("blocks/create.json", UriKind.Relative);
648 var param = new Dictionary<string, string>
650 ["screen_name"] = screenName,
651 ["tweet_mode"] = "extended",
654 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
657 public Task<LazyJson<TwitterUser>> BlocksDestroy(string screenName)
659 var endpoint = new Uri("blocks/destroy.json", UriKind.Relative);
660 var param = new Dictionary<string, string>
662 ["screen_name"] = screenName,
663 ["tweet_mode"] = "extended",
666 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
669 public async Task<TwitterUser> AccountVerifyCredentials()
671 var endpoint = new Uri("account/verify_credentials.json", UriKind.Relative);
672 var param = new Dictionary<string, string>
674 ["include_entities"] = "true",
675 ["include_ext_alt_text"] = "true",
676 ["tweet_mode"] = "extended",
679 var user = await this.Connection.GetAsync<TwitterUser>(endpoint, param, "/account/verify_credentials")
680 .ConfigureAwait(false);
682 this.CurrentUserId = user.Id;
683 this.CurrentScreenName = user.ScreenName;
688 public Task<LazyJson<TwitterUser>> AccountUpdateProfile(string name, string url, string? location, string? description)
690 var endpoint = new Uri("account/update_profile.json", UriKind.Relative);
691 var param = new Dictionary<string, string>
693 ["include_entities"] = "true",
694 ["include_ext_alt_text"] = "true",
695 ["tweet_mode"] = "extended",
699 param["name"] = name;
702 if (location != null)
703 param["location"] = location;
705 if (description != null)
707 // name, location, description に含まれる < > " の文字はTwitter側で除去されるが、
708 // twitter.com の挙動では description でのみ < 等の文字参照を使って表示することができる
709 var escapedDescription = description.Replace("<", "<").Replace(">", ">").Replace("\"", """);
710 param["description"] = escapedDescription;
713 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param);
716 public Task<LazyJson<TwitterUser>> AccountUpdateProfileImage(IMediaItem image)
718 var endpoint = new Uri("account/update_profile_image.json", UriKind.Relative);
719 var param = new Dictionary<string, string>
721 ["include_entities"] = "true",
722 ["include_ext_alt_text"] = "true",
723 ["tweet_mode"] = "extended",
725 var paramMedia = new Dictionary<string, IMediaItem>
730 return this.Connection.PostLazyAsync<TwitterUser>(endpoint, param, paramMedia);
733 public Task<TwitterRateLimits> ApplicationRateLimitStatus()
735 var endpoint = new Uri("application/rate_limit_status.json", UriKind.Relative);
737 return this.Connection.GetAsync<TwitterRateLimits>(endpoint, null, "/application/rate_limit_status");
740 public Task<TwitterConfiguration> Configuration()
742 var endpoint = new Uri("help/configuration.json", UriKind.Relative);
744 return this.Connection.GetAsync<TwitterConfiguration>(endpoint, null, "/help/configuration");
747 public Task<LazyJson<TwitterUploadMediaInit>> MediaUploadInit(long totalBytes, string mediaType, string? mediaCategory = null)
749 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
750 var param = new Dictionary<string, string>
752 ["command"] = "INIT",
753 ["total_bytes"] = totalBytes.ToString(),
754 ["media_type"] = mediaType,
757 if (mediaCategory != null)
758 param["media_category"] = mediaCategory;
760 return this.Connection.PostLazyAsync<TwitterUploadMediaInit>(endpoint, param);
763 public Task MediaUploadAppend(long mediaId, int segmentIndex, IMediaItem media)
765 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
766 var param = new Dictionary<string, string>
768 ["command"] = "APPEND",
769 ["media_id"] = mediaId.ToString(),
770 ["segment_index"] = segmentIndex.ToString(),
772 var paramMedia = new Dictionary<string, IMediaItem>
777 return this.Connection.PostAsync(endpoint, param, paramMedia);
780 public Task<LazyJson<TwitterUploadMediaResult>> MediaUploadFinalize(long mediaId)
782 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
783 var param = new Dictionary<string, string>
785 ["command"] = "FINALIZE",
786 ["media_id"] = mediaId.ToString(),
789 return this.Connection.PostLazyAsync<TwitterUploadMediaResult>(endpoint, param);
792 public Task<TwitterUploadMediaResult> MediaUploadStatus(long mediaId)
794 var endpoint = new Uri("https://upload.twitter.com/1.1/media/upload.json");
795 var param = new Dictionary<string, string>
797 ["command"] = "STATUS",
798 ["media_id"] = mediaId.ToString(),
801 return this.Connection.GetAsync<TwitterUploadMediaResult>(endpoint, param, endpointName: null);
804 public Task MediaMetadataCreate(long mediaId, string altText)
806 var endpoint = new Uri("https://upload.twitter.com/1.1/media/metadata/create.json");
808 var escapedAltText = JsonUtils.EscapeJsonString(altText);
809 var json = $@"{{""media_id"": ""{mediaId}"", ""alt_text"": {{""text"": ""{escapedAltText}""}}}}";
811 return this.Connection.PostJsonAsync(endpoint, json);
814 public OAuthEchoHandler CreateOAuthEchoHandler(Uri authServiceProvider, Uri? realm = null)
815 => ((TwitterApiConnection)this.Connection).CreateOAuthEchoHandler(authServiceProvider, realm);
817 public void Dispose()
818 => this.ApiConnection?.Dispose();