using System.Windows.Forms;
using OpenTween.Api;
using OpenTween.Api.DataModel;
+using OpenTween.Api.GraphQL;
using OpenTween.Api.TwitterV2;
using OpenTween.Connection;
using OpenTween.Models;
private long[] noRTId = Array.Empty<long>();
private readonly TwitterPostFactory postFactory;
+ private readonly PostUrlExpander urlExpander;
- private string? nextCursorDirectMessage = null;
-
- private long previousStatusId = -1L;
+ private string? previousStatusId = null;
public Twitter(TwitterApi api)
{
this.postFactory = new(TabInformations.GetInstance());
+ this.urlExpander = new(ShortUrl.Instance);
this.Api = api;
this.Configuration = TwitterConfiguration.DefaultConfiguration();
this.UpdateUserStats(user);
}
- public void Initialize(string token, string tokenSecret, string username, long userId)
+ public void Initialize(ITwitterCredential credential, string username, long userId)
{
// OAuth認証
- if (MyCommon.IsNullOrEmpty(token) || MyCommon.IsNullOrEmpty(tokenSecret) || MyCommon.IsNullOrEmpty(username))
- {
+ if (credential is TwitterCredentialNone)
Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
- }
+
this.ResetApiStatus();
- this.Api.Initialize(token, tokenSecret, userId, username);
+ this.Api.Initialize(credential, userId, username);
}
public async Task<PostClass?> PostStatus(PostStatusParams param)
return null;
}
- var response = await this.Api.StatusesUpdate(
- param.Text,
- param.InReplyToStatusId,
- param.MediaIds,
- param.AutoPopulateReplyMetadata,
- param.ExcludeReplyUserIds,
- param.AttachmentUrl
- )
- .ConfigureAwait(false);
+ TwitterStatus status;
- var status = await response.LoadJsonAsync()
- .ConfigureAwait(false);
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new CreateTweetRequest
+ {
+ TweetText = param.Text,
+ InReplyToTweetId = param.InReplyToStatusId?.ToTwitterStatusId(),
+ ExcludeReplyUserIds = param.ExcludeReplyUserIds.Select(x => x.ToString()).ToArray(),
+ MediaIds = param.MediaIds.Select(x => x.ToString()).ToArray(),
+ AttachmentUrl = param.AttachmentUrl,
+ };
+
+ status = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ using var response = await this.Api.StatusesUpdate(
+ param.Text,
+ param.InReplyToStatusId?.ToTwitterStatusId(),
+ param.MediaIds,
+ param.AutoPopulateReplyMetadata,
+ param.ExcludeReplyUserIds,
+ param.AttachmentUrl
+ )
+ .ConfigureAwait(false);
+
+ status = await response.LoadJsonAsync()
+ .ConfigureAwait(false);
+ }
this.UpdateUserStats(status.User);
- if (status.Id == this.previousStatusId)
+ if (status.IdStr == this.previousStatusId)
throw new WebApiException("OK:Delaying?");
- this.previousStatusId = status.Id;
+ this.previousStatusId = status.IdStr;
// 投稿したものを返す
var post = this.CreatePostsFromStatusData(status);
return post;
}
+ public async Task DeleteTweet(TwitterStatusId tweetId)
+ {
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new DeleteTweetRequest
+ {
+ TweetId = tweetId,
+ };
+ await request.Send(this.Api.Connection);
+ }
+ else
+ {
+ await this.Api.StatusesDestroy(tweetId)
+ .IgnoreResponse();
+ }
+ }
+
public async Task<long> UploadMedia(IMediaItem item, string? mediaCategory = null)
{
this.CheckAccountState();
_ => "application/octet-stream",
};
- var initResponse = await this.Api.MediaUploadInit(item.Size, mediaType, mediaCategory)
+ using var initResponse = await this.Api.MediaUploadInit(item.Size, mediaType, mediaCategory)
.ConfigureAwait(false);
var initMedia = await initResponse.LoadJsonAsync()
await this.Api.MediaUploadAppend(mediaId, 0, item)
.ConfigureAwait(false);
- var response = await this.Api.MediaUploadFinalize(mediaId)
+ using var response = await this.Api.MediaUploadFinalize(mediaId)
.ConfigureAwait(false);
var media = await response.LoadJsonAsync()
var body = mc.Groups["body"].Value;
var recipientName = mc.Groups["id"].Value;
- var recipient = await this.Api.UsersShow(recipientName)
+ var recipient = await this.GetUserInfo(recipientName)
.ConfigureAwait(false);
- var response = await this.Api.DirectMessagesEventsNew(recipient.Id, body, mediaId)
+ using var response = await this.Api.DirectMessagesEventsNew(recipient.Id, body, mediaId)
.ConfigureAwait(false);
var messageEventSingle = await response.LoadJsonAsync()
.ConfigureAwait(false);
}
- public async Task<PostClass?> PostRetweet(long id, bool read)
+ public async Task<PostClass?> PostRetweet(PostId id, bool read)
{
this.CheckAccountState();
var target = post.RetweetedId ?? id; // 再RTの場合は元発言をRT
- var response = await this.Api.StatusesRetweet(target)
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new CreateRetweetRequest
+ {
+ TweetId = target.ToTwitterStatusId(),
+ };
+ await request.Send(this.Api.Connection).ConfigureAwait(false);
+ return null;
+ }
+
+ using var response = await this.Api.StatusesRetweet(target.ToTwitterStatusId())
.ConfigureAwait(false);
var status = await response.LoadJsonAsync()
// 二重取得回避
lock (this.lockObj)
{
- if (TabInformations.GetInstance().ContainsKey(status.Id))
+ var statusId = new TwitterStatusId(status.IdStr);
+ if (TabInformations.GetInstance().ContainsKey(statusId))
return null;
}
throw new WebApiException("Invalid Json!");
// Retweetしたものを返す
- post = this.CreatePostsFromStatusData(status);
+ return this.CreatePostsFromStatusData(status) with
+ {
+ IsMe = true,
+ IsRead = this.ReadOwnPost ? true : read,
+ IsOwl = false,
+ };
+ }
- // ユーザー情報
- post.IsMe = true;
+ public async Task DeleteRetweet(PostClass post)
+ {
+ if (post.RetweetedId == null)
+ throw new ArgumentException("post is not retweeted status", nameof(post));
- post.IsRead = read;
- post.IsOwl = false;
- if (this.ReadOwnPost) post.IsRead = true;
- post.IsDm = false;
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new DeleteRetweetRequest
+ {
+ SourceTweetId = post.RetweetedId.ToTwitterStatusId(),
+ };
+ await request.Send(this.Api.Connection).ConfigureAwait(false);
+ }
+ else
+ {
+ await this.Api.StatusesDestroy(post.StatusId.ToTwitterStatusId())
+ .IgnoreResponse();
+ }
+ }
- return post;
+ public async Task<TwitterUser> GetUserInfo(string screenName)
+ {
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new UserByScreenNameRequest
+ {
+ ScreenName = screenName,
+ };
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+
+ return response.ToTwitterUser();
+ }
+ else
+ {
+ var user = await this.Api.UsersShow(screenName)
+ .ConfigureAwait(false);
+
+ return user;
+ }
}
public string Username
var count = GetApiResultCount(MyCommon.WORKERTYPE.Timeline, more, startup);
TwitterStatus[] statuses;
- if (SettingManager.Instance.Common.EnableTwitterV2Api)
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new HomeLatestTimelineRequest
+ {
+ Count = count,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+
+ statuses = response.ToTwitterStatuses();
+
+ tab.CursorBottom = response.CursorBottom;
+
+ if (!more)
+ tab.CursorTop = response.CursorTop;
+ }
+ else if (SettingManager.Instance.Common.EnableTwitterV2Api)
{
var request = new GetTimelineRequest(this.UserId)
{
MaxResults = count,
- UntilId = more ? tab.OldestId.ToString() : null,
+ UntilId = more ? tab.OldestId as TwitterStatusId : null,
};
var response = await request.Send(this.Api.Connection)
}
else
{
- var maxId = more ? tab.OldestId : (long?)null;
+ var maxId = more ? tab.OldestId : null;
- statuses = await this.Api.StatusesHomeTimeline(count, maxId)
+ statuses = await this.Api.StatusesHomeTimeline(count, maxId as TwitterStatusId)
.ConfigureAwait(false);
}
var minimumId = this.CreatePostsFromJson(statuses, MyCommon.WORKERTYPE.Timeline, tab, read);
if (minimumId != null)
- tab.OldestId = minimumId.Value;
+ tab.OldestId = minimumId;
}
public async Task GetMentionsTimelineApi(bool read, MentionsTabModel tab, bool more, bool startup)
var count = GetApiResultCount(MyCommon.WORKERTYPE.Reply, more, startup);
TwitterStatus[] statuses;
- if (more)
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
{
- statuses = await this.Api.StatusesMentionsTimeline(count, maxId: tab.OldestId)
+ var request = new NotificationsMentionsRequest
+ {
+ Count = Math.Min(count, 50),
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
+
+ statuses = response.Statuses;
+
+ tab.CursorBottom = response.CursorBottom;
+
+ if (!more)
+ tab.CursorTop = response.CursorTop;
}
else
{
- statuses = await this.Api.StatusesMentionsTimeline(count)
- .ConfigureAwait(false);
+ if (more)
+ {
+ statuses = await this.Api.StatusesMentionsTimeline(count, maxId: tab.OldestId as TwitterStatusId)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ statuses = await this.Api.StatusesMentionsTimeline(count)
+ .ConfigureAwait(false);
+ }
}
var minimumId = this.CreatePostsFromJson(statuses, MyCommon.WORKERTYPE.Reply, tab, read);
if (minimumId != null)
- tab.OldestId = minimumId.Value;
+ tab.OldestId = minimumId;
}
- public async Task GetUserTimelineApi(bool read, string userName, UserTimelineTabModel tab, bool more)
+ public async Task GetUserTimelineApi(bool read, UserTimelineTabModel tab, bool more)
{
this.CheckAccountState();
var count = GetApiResultCount(MyCommon.WORKERTYPE.UserTimeline, more, false);
TwitterStatus[] statuses;
- if (MyCommon.IsNullOrEmpty(userName))
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
{
- var target = tab.ScreenName;
- if (MyCommon.IsNullOrEmpty(target)) return;
- userName = target;
- statuses = await this.Api.StatusesUserTimeline(userName, count)
+ var userId = tab.UserId;
+ if (MyCommon.IsNullOrEmpty(userId))
+ {
+ var user = await this.GetUserInfo(tab.ScreenName)
+ .ConfigureAwait(false);
+
+ userId = user.IdStr;
+ tab.UserId = user.IdStr;
+ }
+
+ var request = new UserTweetsAndRepliesRequest(userId)
+ {
+ Count = count,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
+
+ statuses = response.ToTwitterStatuses()
+ .Where(x => x.User.IdStr == userId) // リプライツリーに含まれる他ユーザーのツイートを除外
+ .ToArray();
+
+ tab.CursorBottom = response.CursorBottom;
+
+ if (!more)
+ tab.CursorTop = response.CursorTop;
}
else
{
if (more)
{
- statuses = await this.Api.StatusesUserTimeline(userName, count, maxId: tab.OldestId)
+ statuses = await this.Api.StatusesUserTimeline(tab.ScreenName, count, maxId: tab.OldestId as TwitterStatusId)
.ConfigureAwait(false);
}
else
{
- statuses = await this.Api.StatusesUserTimeline(userName, count)
+ statuses = await this.Api.StatusesUserTimeline(tab.ScreenName, count)
.ConfigureAwait(false);
}
}
var minimumId = this.CreatePostsFromJson(statuses, MyCommon.WORKERTYPE.UserTimeline, tab, read);
if (minimumId != null)
- tab.OldestId = minimumId.Value;
+ tab.OldestId = minimumId;
}
- public async Task<PostClass> GetStatusApi(bool read, long id)
+ public async Task<PostClass> GetStatusApi(bool read, TwitterStatusId id)
{
this.CheckAccountState();
- var status = await this.Api.StatusesShow(id)
- .ConfigureAwait(false);
+ TwitterStatus status;
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new TweetDetailRequest
+ {
+ FocalTweetId = id,
+ };
+ var tweets = await request.Send(this.Api.Connection).ConfigureAwait(false);
+ status = tweets.Select(x => x.ToTwitterStatus())
+ .Where(x => x.IdStr == id.Id)
+ .FirstOrDefault() ?? throw new WebApiException("Empty result set");
+ }
+ else
+ {
+ status = await this.Api.StatusesShow(id)
+ .ConfigureAwait(false);
+ }
var item = this.CreatePostsFromStatusData(status);
return item;
}
- public async Task GetStatusApi(bool read, long id, TabModel tab)
+ public async Task GetStatusApi(bool read, TwitterStatusId id, TabModel tab)
{
var post = await this.GetStatusApi(read, id)
.ConfigureAwait(false);
=> this.CreatePostsFromStatusData(status, favTweet: false);
private PostClass CreatePostsFromStatusData(TwitterStatus status, bool favTweet)
- => this.postFactory.CreateFromStatus(status, this.UserId, this.followerId, favTweet);
+ {
+ var post = this.postFactory.CreateFromStatus(status, this.UserId, this.followerId, favTweet);
+ _ = this.urlExpander.Expand(post);
- private long? CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabModel? tab, bool read)
+ return post;
+ }
+
+ private PostId? CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabModel? tab, bool read)
{
- long? minimumId = null;
+ PostId? minimumId = null;
- foreach (var status in items)
+ var posts = items.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
+
+ TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);
+
+ foreach (var post in posts)
{
- if (minimumId == null || minimumId.Value > status.Id)
- minimumId = status.Id;
+ if (!post.IsPromoted)
+ {
+ if (minimumId == null || minimumId > post.StatusId)
+ minimumId = post.StatusId;
+ }
// 二重取得回避
lock (this.lockObj)
{
+ var id = post.StatusId;
if (tab == null)
{
- if (TabInformations.GetInstance().ContainsKey(status.Id)) continue;
+ if (TabInformations.GetInstance().ContainsKey(id)) continue;
}
else
{
- if (tab.Contains(status.Id)) continue;
+ if (tab.Contains(id)) continue;
}
}
// RT禁止ユーザーによるもの
if (gType != MyCommon.WORKERTYPE.UserTimeline &&
- status.RetweetedStatus != null && this.noRTId.Contains(status.User.Id)) continue;
-
- var post = this.CreatePostsFromStatusData(status);
+ post.RetweetedByUserId != null && this.noRTId.Contains(post.RetweetedByUserId.Value)) continue;
post.IsRead = read;
if (post.IsMe && !read && this.ReadOwnPost) post.IsRead = true;
return minimumId;
}
- private long? CreatePostsFromSearchJson(TwitterSearchResult items, PublicSearchTabModel tab, bool read, bool more)
+ private PostId? CreatePostsFromSearchJson(TwitterStatus[] statuses, PublicSearchTabModel tab, bool read, bool more)
{
- long? minimumId = null;
+ PostId? minimumId = null;
+
+ var posts = statuses.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
- foreach (var status in items.Statuses)
+ TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);
+
+ foreach (var post in posts)
{
- if (minimumId == null || minimumId.Value > status.Id)
- minimumId = status.Id;
+ if (!post.IsPromoted)
+ {
+ if (minimumId == null || minimumId > post.StatusId)
+ minimumId = post.StatusId;
+
+ if (!more && (tab.SinceId == null || post.StatusId > tab.SinceId))
+ tab.SinceId = post.StatusId;
+ }
- if (!more && status.Id > tab.SinceId) tab.SinceId = status.Id;
// 二重取得回避
lock (this.lockObj)
{
- if (tab.Contains(status.Id)) continue;
+ if (tab.Contains(post.StatusId))
+ continue;
}
- var post = this.CreatePostsFromStatusData(status);
-
post.IsRead = read;
if ((post.IsMe && !read) && this.ReadOwnPost) post.IsRead = true;
// 二重取得回避
lock (this.lockObj)
{
- if (favTab.Contains(status.Id)) continue;
+ if (favTab.Contains(new TwitterStatusId(status.IdStr)))
+ continue;
}
var post = this.CreatePostsFromStatusData(status, true);
var count = GetApiResultCount(MyCommon.WORKERTYPE.List, more, startup);
TwitterStatus[] statuses;
- if (more)
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new ListLatestTweetsTimelineRequest(tab.ListInfo.Id.ToString())
+ {
+ Count = count,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+
+ var convertedStatuses = response.ToTwitterStatuses();
+
+ if (!SettingManager.Instance.Common.IsListsIncludeRts)
+ convertedStatuses = convertedStatuses.Where(x => x.RetweetedStatus == null).ToArray();
+
+ statuses = convertedStatuses.ToArray();
+ tab.CursorBottom = response.CursorBottom;
+
+ if (!more)
+ tab.CursorTop = response.CursorTop;
+ }
+ else if (more)
{
- statuses = await this.Api.ListsStatuses(tab.ListInfo.Id, count, maxId: tab.OldestId, includeRTs: SettingManager.Instance.Common.IsListsIncludeRts)
+ statuses = await this.Api.ListsStatuses(tab.ListInfo.Id, count, maxId: tab.OldestId as TwitterStatusId, includeRTs: SettingManager.Instance.Common.IsListsIncludeRts)
.ConfigureAwait(false);
}
else
var minimumId = this.CreatePostsFromJson(statuses, MyCommon.WORKERTYPE.List, tab, read);
if (minimumId != null)
- tab.OldestId = minimumId.Value;
+ tab.OldestId = minimumId;
}
/// <summary>
/// startStatusId からリプライ先の発言を辿る。発言は posts 以外からは検索しない。
/// </summary>
/// <returns>posts の中から検索されたリプライチェインの末端</returns>
- internal static PostClass FindTopOfReplyChain(IDictionary<long, PostClass> posts, long startStatusId)
+ internal static PostClass FindTopOfReplyChain(IDictionary<PostId, PostClass> posts, PostId startStatusId)
{
if (!posts.ContainsKey(startStatusId))
- throw new ArgumentException("startStatusId (" + startStatusId + ") が posts の中から見つかりませんでした。", nameof(startStatusId));
+ throw new ArgumentException("startStatusId (" + startStatusId.Id + ") が posts の中から見つかりませんでした。", nameof(startStatusId));
var nextPost = posts[startStatusId];
while (nextPost.InReplyToStatusId != null)
{
- if (!posts.ContainsKey(nextPost.InReplyToStatusId.Value))
+ if (!posts.ContainsKey(nextPost.InReplyToStatusId))
break;
- nextPost = posts[nextPost.InReplyToStatusId.Value];
+ nextPost = posts[nextPost.InReplyToStatusId];
}
return nextPost;
if (targetPost.RetweetedId != null)
{
- var originalPost = targetPost.Clone();
- originalPost.StatusId = targetPost.RetweetedId.Value;
- originalPost.RetweetedId = null;
- originalPost.RetweetedBy = null;
+ var originalPost = targetPost with
+ {
+ StatusId = targetPost.RetweetedId,
+ RetweetedId = null,
+ RetweetedBy = null,
+ };
targetPost = originalPost;
}
- var relPosts = new Dictionary<long, PostClass>();
+ var relPosts = new Dictionary<PostId, PostClass>();
if (targetPost.TextFromApi.Contains("@") && targetPost.InReplyToStatusId == null)
{
// 検索結果対応
}
else
{
- p = await this.GetStatusApi(read, targetPost.StatusId)
+ p = await this.GetStatusApi(read, targetPost.StatusId.ToTwitterStatusId())
.ConfigureAwait(false);
targetPost = p;
}
var loopCount = 1;
while (nextPost.InReplyToStatusId != null && loopCount++ <= 20)
{
- var inReplyToId = nextPost.InReplyToStatusId.Value;
+ var inReplyToId = nextPost.InReplyToStatusId;
var inReplyToPost = TabInformations.GetInstance()[inReplyToId];
if (inReplyToPost == null)
{
try
{
- inReplyToPost = await this.GetStatusApi(read, inReplyToId)
+ inReplyToPost = await this.GetStatusApi(read, inReplyToId.ToTwitterStatusId())
.ConfigureAwait(false);
}
catch (WebApiException ex)
.Concat(Twitter.ThirdPartyStatusUrlRegex.Matches(text).Cast<Match>());
foreach (var match in ma)
{
- if (long.TryParse(match.Groups["StatusId"].Value, out var statusId))
+ var statusId = new TwitterStatusId(match.Groups["StatusId"].Value);
+ if (!relPosts.ContainsKey(statusId))
{
- if (relPosts.ContainsKey(statusId))
- continue;
-
var p = TabInformations.GetInstance()[statusId];
if (p == null)
{
continue;
// リプライチェーンが繋がらないツイートは除外
- if (post.InReplyToStatusId == null || !relPosts.ContainsKey(post.InReplyToStatusId.Value))
+ if (post.InReplyToStatusId == null || !relPosts.ContainsKey(post.InReplyToStatusId))
continue;
relPosts.Add(post.StatusId, post);
relPosts.Values.ToList().ForEach(p =>
{
- var post = p.Clone();
+ var post = p with { };
if (post.IsMe && !read && this.ReadOwnPost)
post.IsRead = true;
else
private async Task<PostClass[]> GetConversationPosts(PostClass firstPost, PostClass targetPost)
{
var conversationId = firstPost.StatusId;
- var query = $"conversation_id:{conversationId}";
+ var query = $"conversation_id:{conversationId.Id}";
if (targetPost.InReplyToUser != null && targetPost.InReplyToUser != targetPost.ScreenName)
query += $" (from:{targetPost.ScreenName} to:{targetPost.InReplyToUser}) OR (from:{targetPost.InReplyToUser} to:{targetPost.ScreenName})";
else
query += $" from:{targetPost.ScreenName} to:{targetPost.ScreenName}";
- var statuses = await this.Api.SearchTweets(query, count: 100)
- .ConfigureAwait(false);
+ TwitterStatus[] statuses;
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
+ {
+ var request = new SearchTimelineRequest(query);
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
- return statuses.Statuses.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
+ statuses = response.ToTwitterStatuses();
+ }
+ else
+ {
+ var response = await this.Api.SearchTweets(query, count: 100)
+ .ConfigureAwait(false);
+
+ statuses = response.Statuses;
+ }
+
+ return statuses.Select(x => this.CreatePostsFromStatusData(x)).ToArray();
}
public async Task GetSearch(bool read, PublicSearchTabModel tab, bool more)
{
var count = GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, more, false);
- long? maxId = null;
- long? sinceId = null;
- if (more)
+ TwitterStatus[] statuses;
+ if (this.Api.AuthType == APIAuthType.TwitterComCookie)
{
- maxId = tab.OldestId - 1;
+ var query = tab.SearchWords;
+
+ if (!MyCommon.IsNullOrEmpty(tab.SearchLang))
+ query = $"({query}) lang:{tab.SearchLang}";
+
+ var request = new SearchTimelineRequest(query)
+ {
+ Count = count,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+
+ statuses = response.ToTwitterStatuses();
+
+ tab.CursorBottom = response.CursorBottom;
+
+ if (!more)
+ tab.CursorTop = response.CursorTop;
}
else
{
- sinceId = tab.SinceId;
- }
+ TwitterStatusId? maxId = null;
+ TwitterStatusId? sinceId = null;
+ if (more)
+ {
+ maxId = tab.OldestId as TwitterStatusId;
+ }
+ else
+ {
+ sinceId = tab.SinceId as TwitterStatusId;
+ }
- var searchResult = await this.Api.SearchTweets(tab.SearchWords, tab.SearchLang, count, maxId, sinceId)
- .ConfigureAwait(false);
+ var searchResult = await this.Api.SearchTweets(tab.SearchWords, tab.SearchLang, count, maxId, sinceId)
+ .ConfigureAwait(false);
+
+ statuses = searchResult.Statuses;
+ }
if (!TabInformations.GetInstance().ContainsTab(tab))
return;
- var minimumId = this.CreatePostsFromSearchJson(searchResult, tab, read, more);
+ var minimumId = this.CreatePostsFromSearchJson(statuses, tab, read, more);
if (minimumId != null)
- tab.OldestId = minimumId.Value;
+ tab.OldestId = minimumId;
}
- public async Task GetDirectMessageEvents(bool read, bool backward)
+ public async Task GetDirectMessageEvents(bool read, DirectMessagesTabModel dmTab, bool backward)
{
this.CheckAccountState();
this.CheckAccessLevel(TwitterApiAccessLevel.ReadWriteAndDirectMessage);
TwitterMessageEventList eventList;
if (backward)
{
- eventList = await this.Api.DirectMessagesEventsList(count, this.nextCursorDirectMessage)
+ eventList = await this.Api.DirectMessagesEventsList(count, dmTab.NextCursor)
.ConfigureAwait(false);
}
else
.ConfigureAwait(false);
}
- this.nextCursorDirectMessage = eventList.NextCursor;
+ dmTab.NextCursor = eventList.NextCursor;
await this.CreateDirectMessagesEventFromJson(eventList, read)
.ConfigureAwait(false);
foreach (var eventItem in events)
{
var post = this.postFactory.CreateFromDirectMessageEvent(eventItem, users, apps, this.UserId);
+ _ = this.urlExpander.Expand(post);
post.IsRead = read;
if (post.IsMe && !read && this.ReadOwnPost)
public async Task<ListElement> EditList(long listId, string new_name, bool isPrivate, string description)
{
- var response = await this.Api.ListsUpdate(listId, new_name, description, isPrivate)
+ using var response = await this.Api.ListsUpdate(listId, new_name, description, isPrivate)
.ConfigureAwait(false);
var list = await response.LoadJsonAsync()
{
this.CheckAccountState();
- var response = await this.Api.ListsCreate(listName, description, isPrivate)
+ using var response = await this.Api.ListsCreate(listName, description, isPrivate)
.ConfigureAwait(false);
var list = await response.LoadJsonAsync()
public string[] GetHashList()
=> this.postFactory.GetReceivedHashtags();
- public string AccessToken
- => ((TwitterApiConnection)this.Api.Connection).AccessToken;
-
- public string AccessTokenSecret
- => ((TwitterApiConnection)this.Api.Connection).AccessSecret;
-
private void CheckAccountState()
{
if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid)