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;
28 using System.Net.Http;
30 using System.Threading;
31 using System.Threading.Tasks;
32 using OpenTween.Api.DataModel;
33 using OpenTween.Connection;
34 using OpenTween.Models;
36 namespace OpenTween.Api
38 public sealed class TwitterApi : IDisposable
40 public long CurrentUserId { get; private set; }
42 public string CurrentScreenName { get; private set; } = "";
44 public IApiConnection Connection => this.ApiConnection;
46 internal IApiConnection ApiConnection;
48 public APIAuthType AuthType { get; private set; } = APIAuthType.None;
51 => this.ApiConnection = new TwitterApiConnection(new TwitterCredentialNone());
53 public void Initialize(ITwitterCredential credential, long userId, string screenName)
55 this.AuthType = credential.AuthType;
57 var newInstance = new TwitterApiConnection(credential);
58 var oldInstance = Interlocked.Exchange(ref this.ApiConnection, newInstance);
59 oldInstance?.Dispose();
61 this.CurrentUserId = userId;
62 this.CurrentScreenName = screenName;
65 public async Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
67 var param = new Dictionary<string, string>
69 ["include_entities"] = "true",
70 ["include_ext_alt_text"] = "true",
71 ["tweet_mode"] = "extended",
75 param["count"] = count.ToString();
77 param["max_id"] = maxId.Id;
79 param["since_id"] = sinceId.Id;
81 var request = new GetRequest
83 RequestUri = new("statuses/home_timeline.json", UriKind.Relative),
85 EndpointName = "/statuses/home_timeline",
88 using var response = await this.Connection.SendAsync(request)
89 .ConfigureAwait(false);
91 return await response.ReadAsJson<TwitterStatus[]>()
92 .ConfigureAwait(false);
95 public async Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
97 var param = new Dictionary<string, string>
99 ["include_entities"] = "true",
100 ["include_ext_alt_text"] = "true",
101 ["tweet_mode"] = "extended",
105 param["count"] = count.ToString();
107 param["max_id"] = maxId.Id;
109 param["since_id"] = sinceId.Id;
111 var request = new GetRequest
113 RequestUri = new("statuses/mentions_timeline.json", UriKind.Relative),
115 EndpointName = "/statuses/mentions_timeline",
118 using var response = await this.Connection.SendAsync(request)
119 .ConfigureAwait(false);
121 return await response.ReadAsJson<TwitterStatus[]>()
122 .ConfigureAwait(false);
125 public async Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
127 var param = new Dictionary<string, string>
129 ["screen_name"] = screenName,
130 ["include_rts"] = "true",
131 ["include_entities"] = "true",
132 ["include_ext_alt_text"] = "true",
133 ["tweet_mode"] = "extended",
137 param["count"] = count.ToString();
139 param["max_id"] = maxId.Id;
141 param["since_id"] = sinceId.Id;
143 var request = new GetRequest
145 RequestUri = new("statuses/user_timeline.json", UriKind.Relative),
147 EndpointName = "/statuses/user_timeline",
150 using var response = await this.Connection.SendAsync(request)
151 .ConfigureAwait(false);
153 return await response.ReadAsJson<TwitterStatus[]>()
154 .ConfigureAwait(false);
157 public async Task<TwitterStatus> StatusesShow(TwitterStatusId statusId)
159 var request = new GetRequest
161 RequestUri = new("statuses/show.json", UriKind.Relative),
162 Query = new Dictionary<string, string>
164 ["id"] = statusId.Id,
165 ["include_entities"] = "true",
166 ["include_ext_alt_text"] = "true",
167 ["tweet_mode"] = "extended",
169 EndpointName = "/statuses/show/:id",
172 using var response = await this.Connection.SendAsync(request)
173 .ConfigureAwait(false);
175 return await response.ReadAsJson<TwitterStatus>()
176 .ConfigureAwait(false);
179 public async Task<TwitterStatus[]> StatusesLookup(IReadOnlyList<string> statusIds)
181 var request = new GetRequest
183 RequestUri = new("statuses/lookup.json", UriKind.Relative),
184 Query = new Dictionary<string, string>
186 ["id"] = string.Join(",", statusIds),
187 ["include_entities"] = "true",
188 ["include_ext_alt_text"] = "true",
189 ["tweet_mode"] = "extended",
191 EndpointName = "/statuses/lookup",
194 using var response = await this.Connection.SendAsync(request)
195 .ConfigureAwait(false);
197 return await response.ReadAsJson<TwitterStatus[]>()
198 .ConfigureAwait(false);
201 public async Task<LazyJson<TwitterStatus>> StatusesUpdate(
203 TwitterStatusId? replyToId,
204 IReadOnlyList<long>? mediaIds,
205 bool? autoPopulateReplyMetadata = null,
206 IReadOnlyList<long>? excludeReplyUserIds = null,
207 string? attachmentUrl = null)
209 var param = new Dictionary<string, string>
212 ["include_entities"] = "true",
213 ["include_ext_alt_text"] = "true",
214 ["tweet_mode"] = "extended",
217 if (replyToId != null)
218 param["in_reply_to_status_id"] = replyToId.Id;
219 if (mediaIds != null && mediaIds.Count > 0)
220 param.Add("media_ids", string.Join(",", mediaIds));
221 if (autoPopulateReplyMetadata != null)
222 param["auto_populate_reply_metadata"] = autoPopulateReplyMetadata.Value ? "true" : "false";
223 if (excludeReplyUserIds != null && excludeReplyUserIds.Count > 0)
224 param["exclude_reply_user_ids"] = string.Join(",", excludeReplyUserIds);
225 if (attachmentUrl != null)
226 param["attachment_url"] = attachmentUrl;
228 var request = new PostRequest
230 RequestUri = new("statuses/update.json", UriKind.Relative),
234 using var response = await this.Connection.SendAsync(request)
235 .ConfigureAwait(false);
237 return response.ReadAsLazyJson<TwitterStatus>();
240 public async Task<LazyJson<TwitterStatus>> StatusesDestroy(TwitterStatusId statusId)
242 var request = new PostRequest
244 RequestUri = new("statuses/destroy.json", UriKind.Relative),
245 Query = new Dictionary<string, string>
247 ["id"] = statusId.Id,
251 using var response = await this.Connection.SendAsync(request)
252 .ConfigureAwait(false);
254 return response.ReadAsLazyJson<TwitterStatus>();
257 public async Task<LazyJson<TwitterStatus>> StatusesRetweet(TwitterStatusId statusId)
259 var request = new PostRequest
261 RequestUri = new("statuses/retweet.json", UriKind.Relative),
262 Query = new Dictionary<string, string>
264 ["id"] = statusId.Id,
265 ["include_entities"] = "true",
266 ["include_ext_alt_text"] = "true",
267 ["tweet_mode"] = "extended",
271 using var response = await this.Connection.SendAsync(request)
272 .ConfigureAwait(false);
274 return response.ReadAsLazyJson<TwitterStatus>();
277 public async Task<TwitterSearchResult> SearchTweets(string query, string? lang = null, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
279 var param = new Dictionary<string, string>
282 ["result_type"] = "recent",
283 ["include_entities"] = "true",
284 ["include_ext_alt_text"] = "true",
285 ["tweet_mode"] = "extended",
289 param["lang"] = lang;
291 param["count"] = count.ToString();
293 param["max_id"] = maxId.Id;
295 param["since_id"] = sinceId.Id;
297 var request = new GetRequest
299 RequestUri = new("search/tweets.json", UriKind.Relative),
301 EndpointName = "/search/tweets",
304 using var response = await this.Connection.SendAsync(request)
305 .ConfigureAwait(false);
307 return await response.ReadAsJson<TwitterSearchResult>()
308 .ConfigureAwait(false);
311 public async Task<TwitterLists> ListsOwnerships(string screenName, long? cursor = null, int? count = null)
313 var param = new Dictionary<string, string>
315 ["screen_name"] = screenName,
319 param["cursor"] = cursor.ToString();
321 param["count"] = count.ToString();
323 var request = new GetRequest
325 RequestUri = new("lists/ownerships.json", UriKind.Relative),
327 EndpointName = "/lists/ownerships",
330 using var response = await this.Connection.SendAsync(request)
331 .ConfigureAwait(false);
333 return await response.ReadAsJson<TwitterLists>()
334 .ConfigureAwait(false);
337 public async Task<TwitterLists> ListsSubscriptions(string screenName, long? cursor = null, int? count = null)
339 var param = new Dictionary<string, string>
341 ["screen_name"] = screenName,
345 param["cursor"] = cursor.ToString();
347 param["count"] = count.ToString();
349 var request = new GetRequest
351 RequestUri = new("lists/subscriptions.json", UriKind.Relative),
353 EndpointName = "/lists/subscriptions",
356 using var response = await this.Connection.SendAsync(request)
357 .ConfigureAwait(false);
359 return await response.ReadAsJson<TwitterLists>()
360 .ConfigureAwait(false);
363 public async Task<TwitterLists> ListsMemberships(string screenName, long? cursor = null, int? count = null, bool? filterToOwnedLists = null)
365 var param = new Dictionary<string, string>
367 ["screen_name"] = screenName,
371 param["cursor"] = cursor.ToString();
373 param["count"] = count.ToString();
374 if (filterToOwnedLists != null)
375 param["filter_to_owned_lists"] = filterToOwnedLists.Value ? "true" : "false";
377 var request = new GetRequest
379 RequestUri = new("lists/memberships.json", UriKind.Relative),
381 EndpointName = "/lists/memberships",
384 using var response = await this.Connection.SendAsync(request)
385 .ConfigureAwait(false);
387 return await response.ReadAsJson<TwitterLists>()
388 .ConfigureAwait(false);
391 public async Task<LazyJson<TwitterList>> ListsCreate(string name, string? description = null, bool? @private = null)
393 var param = new Dictionary<string, string>
398 if (description != null)
399 param["description"] = description;
400 if (@private != null)
401 param["mode"] = @private.Value ? "private" : "public";
403 var request = new PostRequest
405 RequestUri = new("lists/create.json", UriKind.Relative),
409 using var response = await this.Connection.SendAsync(request)
410 .ConfigureAwait(false);
412 return response.ReadAsLazyJson<TwitterList>();
415 public async Task<LazyJson<TwitterList>> ListsUpdate(long listId, string? name = null, string? description = null, bool? @private = null)
417 var param = new Dictionary<string, string>
419 ["list_id"] = listId.ToString(),
423 param["name"] = name;
424 if (description != null)
425 param["description"] = description;
426 if (@private != null)
427 param["mode"] = @private.Value ? "private" : "public";
429 var request = new PostRequest
431 RequestUri = new("lists/update.json", UriKind.Relative),
435 using var response = await this.Connection.SendAsync(request)
436 .ConfigureAwait(false);
438 return response.ReadAsLazyJson<TwitterList>();
441 public async Task<LazyJson<TwitterList>> ListsDestroy(long listId)
443 var request = new PostRequest
445 RequestUri = new("lists/destroy.json", UriKind.Relative),
446 Query = new Dictionary<string, string>
448 ["list_id"] = listId.ToString(),
452 using var response = await this.Connection.SendAsync(request)
453 .ConfigureAwait(false);
455 return response.ReadAsLazyJson<TwitterList>();
458 public async Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null, bool? includeRTs = null)
460 var param = new Dictionary<string, string>
462 ["list_id"] = listId.ToString(),
463 ["include_entities"] = "true",
464 ["include_ext_alt_text"] = "true",
465 ["tweet_mode"] = "extended",
469 param["count"] = count.ToString();
471 param["max_id"] = maxId.Id;
473 param["since_id"] = sinceId.Id;
474 if (includeRTs != null)
475 param["include_rts"] = includeRTs.Value ? "true" : "false";
477 var request = new GetRequest
479 RequestUri = new("lists/statuses.json", UriKind.Relative),
481 EndpointName = "/lists/statuses",
484 using var response = await this.Connection.SendAsync(request)
485 .ConfigureAwait(false);
487 return await response.ReadAsJson<TwitterStatus[]>()
488 .ConfigureAwait(false);
491 public async Task<TwitterUsers> ListsMembers(long listId, long? cursor = null)
493 var param = new Dictionary<string, string>
495 ["list_id"] = listId.ToString(),
496 ["include_entities"] = "true",
497 ["include_ext_alt_text"] = "true",
498 ["tweet_mode"] = "extended",
502 param["cursor"] = cursor.ToString();
504 var request = new GetRequest
506 RequestUri = new("lists/members.json", UriKind.Relative),
508 EndpointName = "/lists/members",
511 using var response = await this.Connection.SendAsync(request)
512 .ConfigureAwait(false);
514 return await response.ReadAsJson<TwitterUsers>()
515 .ConfigureAwait(false);
518 public async Task<TwitterUser> ListsMembersShow(long listId, string screenName)
520 var request = new GetRequest
522 RequestUri = new("lists/members/show.json", UriKind.Relative),
523 Query = new Dictionary<string, string>
525 ["list_id"] = listId.ToString(),
526 ["screen_name"] = screenName,
527 ["include_entities"] = "true",
528 ["include_ext_alt_text"] = "true",
529 ["tweet_mode"] = "extended",
531 EndpointName = "/lists/members/show",
534 using var response = await this.Connection.SendAsync(request)
535 .ConfigureAwait(false);
537 return await response.ReadAsJson<TwitterUser>()
538 .ConfigureAwait(false);
541 public async Task<LazyJson<TwitterUser>> ListsMembersCreate(long listId, string screenName)
543 var request = new PostRequest
545 RequestUri = new("lists/members/create.json", UriKind.Relative),
546 Query = new Dictionary<string, string>
548 ["list_id"] = listId.ToString(),
549 ["screen_name"] = screenName,
550 ["include_entities"] = "true",
551 ["include_ext_alt_text"] = "true",
552 ["tweet_mode"] = "extended",
556 using var response = await this.Connection.SendAsync(request)
557 .ConfigureAwait(false);
559 return response.ReadAsLazyJson<TwitterUser>();
562 public async Task<LazyJson<TwitterUser>> ListsMembersDestroy(long listId, string screenName)
564 var request = new PostRequest
566 RequestUri = new("lists/members/destroy.json", UriKind.Relative),
567 Query = new Dictionary<string, string>
569 ["list_id"] = listId.ToString(),
570 ["screen_name"] = screenName,
571 ["include_entities"] = "true",
572 ["include_ext_alt_text"] = "true",
573 ["tweet_mode"] = "extended",
577 using var response = await this.Connection.SendAsync(request)
578 .ConfigureAwait(false);
580 return response.ReadAsLazyJson<TwitterUser>();
583 public async Task<TwitterMessageEventList> DirectMessagesEventsList(int? count = null, string? cursor = null)
585 var param = new Dictionary<string, string>();
588 param["count"] = count.ToString();
590 param["cursor"] = cursor;
592 var request = new GetRequest
594 RequestUri = new("direct_messages/events/list.json", UriKind.Relative),
596 EndpointName = "/direct_messages/events/list",
599 using var response = await this.Connection.SendAsync(request)
600 .ConfigureAwait(false);
602 return await response.ReadAsJson<TwitterMessageEventList>()
603 .ConfigureAwait(false);
606 public async Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
611 attachment = ",\r\n" + $$"""
615 "id": "{{JsonUtils.EscapeJsonString(mediaId.ToString())}}"
624 "type": "message_create",
627 "recipient_id": "{{JsonUtils.EscapeJsonString(recipientId.ToString())}}"
630 "text": "{{JsonUtils.EscapeJsonString(text)}}"{{attachment}}
637 var request = new PostJsonRequest
639 RequestUri = new("direct_messages/events/new.json", UriKind.Relative),
643 var response = await this.Connection.SendAsync(request)
644 .ConfigureAwait(false);
646 return response.ReadAsLazyJson<TwitterMessageEventSingle>();
649 public async Task DirectMessagesEventsDestroy(TwitterDirectMessageId eventId)
651 var request = new DeleteRequest
653 RequestUri = new("direct_messages/events/destroy.json", UriKind.Relative),
654 Query = new Dictionary<string, string>
660 await this.Connection.SendAsync(request)
662 .ConfigureAwait(false);
665 public async Task<TwitterUser> UsersShow(string screenName)
667 var request = new GetRequest
669 RequestUri = new("users/show.json", UriKind.Relative),
670 Query = new Dictionary<string, string>
672 ["screen_name"] = screenName,
673 ["include_entities"] = "true",
674 ["include_ext_alt_text"] = "true",
675 ["tweet_mode"] = "extended",
677 EndpointName = "/users/show/:id",
680 using var response = await this.Connection.SendAsync(request)
681 .ConfigureAwait(false);
683 return await response.ReadAsJson<TwitterUser>()
684 .ConfigureAwait(false);
687 public async Task<TwitterUser[]> UsersLookup(IReadOnlyList<string> userIds)
689 var request = new GetRequest
691 RequestUri = new("users/lookup.json", UriKind.Relative),
692 Query = new Dictionary<string, string>
694 ["user_id"] = string.Join(",", userIds),
695 ["include_entities"] = "true",
696 ["include_ext_alt_text"] = "true",
697 ["tweet_mode"] = "extended",
699 EndpointName = "/users/lookup",
702 using var response = await this.Connection.SendAsync(request)
703 .ConfigureAwait(false);
705 return await response.ReadAsJson<TwitterUser[]>()
706 .ConfigureAwait(false);
709 public async Task<LazyJson<TwitterUser>> UsersReportSpam(string screenName)
711 var request = new PostRequest
713 RequestUri = new("users/report_spam.json", UriKind.Relative),
714 Query = new Dictionary<string, string>
716 ["screen_name"] = screenName,
717 ["tweet_mode"] = "extended",
721 using var response = await this.Connection.SendAsync(request)
722 .ConfigureAwait(false);
724 return response.ReadAsLazyJson<TwitterUser>();
727 public async Task<TwitterStatus[]> FavoritesList(int? count = null, long? maxId = null, long? sinceId = null)
729 var param = new Dictionary<string, string>
731 ["include_entities"] = "true",
732 ["include_ext_alt_text"] = "true",
733 ["tweet_mode"] = "extended",
737 param["count"] = count.ToString();
739 param["max_id"] = maxId.ToString();
741 param["since_id"] = sinceId.ToString();
743 var request = new GetRequest
745 RequestUri = new("favorites/list.json", UriKind.Relative),
747 EndpointName = "/favorites/list",
750 using var response = await this.Connection.SendAsync(request)
751 .ConfigureAwait(false);
753 return await response.ReadAsJson<TwitterStatus[]>()
754 .ConfigureAwait(false);
757 public async Task<LazyJson<TwitterStatus>> FavoritesCreate(TwitterStatusId statusId)
759 var request = new PostRequest
761 RequestUri = new("favorites/create.json", UriKind.Relative),
762 Query = new Dictionary<string, string>
764 ["id"] = statusId.Id,
765 ["tweet_mode"] = "extended",
769 using var response = await this.Connection.SendAsync(request)
770 .ConfigureAwait(false);
772 return response.ReadAsLazyJson<TwitterStatus>();
775 public async Task<LazyJson<TwitterStatus>> FavoritesDestroy(TwitterStatusId statusId)
777 var request = new PostRequest
779 RequestUri = new("favorites/destroy.json", UriKind.Relative),
780 Query = new Dictionary<string, string>
782 ["id"] = statusId.Id,
783 ["tweet_mode"] = "extended",
787 using var response = await this.Connection.SendAsync(request)
788 .ConfigureAwait(false);
790 return response.ReadAsLazyJson<TwitterStatus>();
793 public async Task<TwitterFriendship> FriendshipsShow(string sourceScreenName, string targetScreenName)
795 var request = new GetRequest
797 RequestUri = new("friendships/show.json", UriKind.Relative),
798 Query = new Dictionary<string, string>
800 ["source_screen_name"] = sourceScreenName,
801 ["target_screen_name"] = targetScreenName,
803 EndpointName = "/friendships/show",
806 using var response = await this.Connection.SendAsync(request)
807 .ConfigureAwait(false);
809 return await response.ReadAsJson<TwitterFriendship>()
810 .ConfigureAwait(false);
813 public async Task<LazyJson<TwitterFriendship>> FriendshipsCreate(string screenName)
815 var request = new PostRequest
817 RequestUri = new("friendships/create.json", UriKind.Relative),
818 Query = new Dictionary<string, string>
820 ["screen_name"] = screenName,
824 using var response = await this.Connection.SendAsync(request)
825 .ConfigureAwait(false);
827 return response.ReadAsLazyJson<TwitterFriendship>();
830 public async Task<LazyJson<TwitterFriendship>> FriendshipsDestroy(string screenName)
832 var request = new PostRequest
834 RequestUri = new("friendships/destroy.json", UriKind.Relative),
835 Query = new Dictionary<string, string>
837 ["screen_name"] = screenName,
841 using var response = await this.Connection.SendAsync(request)
842 .ConfigureAwait(false);
844 return response.ReadAsLazyJson<TwitterFriendship>();
847 public async Task<long[]> NoRetweetIds()
849 var request = new GetRequest
851 RequestUri = new("friendships/no_retweets/ids.json", UriKind.Relative),
852 EndpointName = "/friendships/no_retweets/ids",
855 using var response = await this.Connection.SendAsync(request)
856 .ConfigureAwait(false);
858 return await response.ReadAsJson<long[]>()
859 .ConfigureAwait(false);
862 public async Task<TwitterIds> FollowersIds(long? cursor = null)
864 var param = new Dictionary<string, string>();
867 param["cursor"] = cursor.ToString();
869 var request = new GetRequest
871 RequestUri = new("followers/ids.json", UriKind.Relative),
873 EndpointName = "/followers/ids",
876 using var response = await this.Connection.SendAsync(request)
877 .ConfigureAwait(false);
879 return await response.ReadAsJson<TwitterIds>()
880 .ConfigureAwait(false);
883 public async Task<TwitterIds> MutesUsersIds(long? cursor = null)
885 var param = new Dictionary<string, string>();
888 param["cursor"] = cursor.ToString();
890 var request = new GetRequest
892 RequestUri = new("mutes/users/ids.json", UriKind.Relative),
894 EndpointName = "/mutes/users/ids",
897 using var response = await this.Connection.SendAsync(request)
898 .ConfigureAwait(false);
900 return await response.ReadAsJson<TwitterIds>()
901 .ConfigureAwait(false);
904 public async Task<TwitterIds> BlocksIds(long? cursor = null)
906 var param = new Dictionary<string, string>();
909 param["cursor"] = cursor.ToString();
911 var request = new GetRequest
913 RequestUri = new("blocks/ids.json", UriKind.Relative),
915 EndpointName = "/blocks/ids",
918 using var response = await this.Connection.SendAsync(request)
919 .ConfigureAwait(false);
921 return await response.ReadAsJson<TwitterIds>()
922 .ConfigureAwait(false);
925 public async Task<LazyJson<TwitterUser>> BlocksCreate(string screenName)
927 var request = new PostRequest
929 RequestUri = new("blocks/create.json", UriKind.Relative),
930 Query = new Dictionary<string, string>
932 ["screen_name"] = screenName,
933 ["tweet_mode"] = "extended",
937 using var response = await this.Connection.SendAsync(request)
938 .ConfigureAwait(false);
940 return response.ReadAsLazyJson<TwitterUser>();
943 public async Task<LazyJson<TwitterUser>> BlocksDestroy(string screenName)
945 var request = new PostRequest
947 RequestUri = new("blocks/destroy.json", UriKind.Relative),
948 Query = new Dictionary<string, string>
950 ["screen_name"] = screenName,
951 ["tweet_mode"] = "extended",
955 using var response = await this.Connection.SendAsync(request)
956 .ConfigureAwait(false);
958 return response.ReadAsLazyJson<TwitterUser>();
961 public async Task<TwitterUser> AccountVerifyCredentials()
963 var request = new GetRequest
965 RequestUri = new("account/verify_credentials.json", UriKind.Relative),
966 Query = new Dictionary<string, string>
968 ["include_entities"] = "true",
969 ["include_ext_alt_text"] = "true",
970 ["tweet_mode"] = "extended",
972 EndpointName = "/account/verify_credentials",
975 using var response = await this.Connection.SendAsync(request)
976 .ConfigureAwait(false);
978 var user = await response.ReadAsJson<TwitterUser>()
979 .ConfigureAwait(false);
981 this.CurrentUserId = user.Id;
982 this.CurrentScreenName = user.ScreenName;
987 public async Task<LazyJson<TwitterUser>> AccountUpdateProfile(string name, string url, string? location, string? description)
989 var param = new Dictionary<string, string>
991 ["include_entities"] = "true",
992 ["include_ext_alt_text"] = "true",
993 ["tweet_mode"] = "extended",
997 param["name"] = name;
1000 if (location != null)
1001 param["location"] = location;
1003 if (description != null)
1005 // name, location, description に含まれる < > " の文字はTwitter側で除去されるが、
1006 // twitter.com の挙動では description でのみ < 等の文字参照を使って表示することができる
1007 var escapedDescription = description.Replace("<", "<").Replace(">", ">").Replace("\"", """);
1008 param["description"] = escapedDescription;
1011 var request = new PostRequest
1013 RequestUri = new("account/update_profile.json", UriKind.Relative),
1017 using var response = await this.Connection.SendAsync(request)
1018 .ConfigureAwait(false);
1020 return response.ReadAsLazyJson<TwitterUser>();
1023 public async Task<LazyJson<TwitterUser>> AccountUpdateProfileImage(IMediaItem image)
1025 var request = new PostMultipartRequest
1027 RequestUri = new("account/update_profile_image.json", UriKind.Relative),
1028 Query = new Dictionary<string, string>
1030 ["include_entities"] = "true",
1031 ["include_ext_alt_text"] = "true",
1032 ["tweet_mode"] = "extended",
1034 Media = new Dictionary<string, IMediaItem>
1040 using var response = await this.Connection.SendAsync(request)
1041 .ConfigureAwait(false);
1043 return response.ReadAsLazyJson<TwitterUser>();
1046 public async Task<TwitterRateLimits> ApplicationRateLimitStatus()
1048 var request = new GetRequest
1050 RequestUri = new("application/rate_limit_status.json", UriKind.Relative),
1051 EndpointName = "/application/rate_limit_status",
1054 using var response = await this.Connection.SendAsync(request)
1055 .ConfigureAwait(false);
1057 return await response.ReadAsJson<TwitterRateLimits>()
1058 .ConfigureAwait(false);
1061 public async Task<TwitterConfiguration> Configuration()
1063 var request = new GetRequest
1065 RequestUri = new("help/configuration.json", UriKind.Relative),
1066 EndpointName = "/help/configuration",
1069 using var response = await this.Connection.SendAsync(request)
1070 .ConfigureAwait(false);
1072 return await response.ReadAsJson<TwitterConfiguration>()
1073 .ConfigureAwait(false);
1076 public async Task<LazyJson<TwitterUploadMediaInit>> MediaUploadInit(long totalBytes, string mediaType, string? mediaCategory = null)
1078 var param = new Dictionary<string, string>
1080 ["command"] = "INIT",
1081 ["total_bytes"] = totalBytes.ToString(),
1082 ["media_type"] = mediaType,
1085 if (mediaCategory != null)
1086 param["media_category"] = mediaCategory;
1088 var request = new PostRequest
1090 RequestUri = new("https://upload.twitter.com/1.1/media/upload.json"),
1094 using var response = await this.Connection.SendAsync(request)
1095 .ConfigureAwait(false);
1097 return response.ReadAsLazyJson<TwitterUploadMediaInit>();
1100 public async Task MediaUploadAppend(long mediaId, int segmentIndex, IMediaItem media)
1102 var request = new PostMultipartRequest
1104 RequestUri = new("https://upload.twitter.com/1.1/media/upload.json"),
1105 Query = new Dictionary<string, string>
1107 ["command"] = "APPEND",
1108 ["media_id"] = mediaId.ToString(),
1109 ["segment_index"] = segmentIndex.ToString(),
1111 Media = new Dictionary<string, IMediaItem>
1117 await this.Connection.SendAsync(request)
1119 .ConfigureAwait(false);
1122 public async Task<LazyJson<TwitterUploadMediaResult>> MediaUploadFinalize(long mediaId)
1124 var request = new PostRequest
1126 RequestUri = new("https://upload.twitter.com/1.1/media/upload.json"),
1127 Query = new Dictionary<string, string>
1129 ["command"] = "FINALIZE",
1130 ["media_id"] = mediaId.ToString(),
1134 using var response = await this.Connection.SendAsync(request)
1135 .ConfigureAwait(false);
1137 return response.ReadAsLazyJson<TwitterUploadMediaResult>();
1140 public async Task<TwitterUploadMediaResult> MediaUploadStatus(long mediaId)
1142 var request = new GetRequest
1144 RequestUri = new("https://upload.twitter.com/1.1/media/upload.json"),
1145 Query = new Dictionary<string, string>
1147 ["command"] = "STATUS",
1148 ["media_id"] = mediaId.ToString(),
1152 using var response = await this.Connection.SendAsync(request)
1153 .ConfigureAwait(false);
1155 return await response.ReadAsJson<TwitterUploadMediaResult>()
1156 .ConfigureAwait(false);
1159 public async Task MediaMetadataCreate(long mediaId, string altText)
1161 var escapedAltText = JsonUtils.EscapeJsonString(altText);
1162 var request = new PostJsonRequest
1164 RequestUri = new("https://upload.twitter.com/1.1/media/metadata/create.json"),
1165 JsonString = $$$"""{"media_id": "{{{mediaId}}}", "alt_text": {"text": "{{{escapedAltText}}}"}}""",
1168 await this.Connection.SendAsync(request)
1170 .ConfigureAwait(false);
1173 public OAuthEchoHandler CreateOAuthEchoHandler(HttpMessageHandler innerHandler, Uri authServiceProvider, Uri? realm = null)
1174 => ((TwitterApiConnection)this.Connection).CreateOAuthEchoHandler(innerHandler, authServiceProvider, realm);
1176 public void Dispose()
1177 => this.ApiConnection?.Dispose();