private readonly TwitterPostFactory postFactory;
- private long previousStatusId = -1L;
+ private string? previousStatusId = null;
public Twitter(TwitterApi api)
{
this.Api.Initialize(token, tokenSecret, userId, username);
}
- public void Initialize(TwitterAppToken appToken, 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(appToken, token, tokenSecret, userId, username);
+ this.Api.Initialize(credential, userId, username);
}
public async Task<PostClass?> PostStatus(PostStatusParams param)
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);
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)
}
}
+ public async Task<TwitterUser> GetUserInfo(string screenName)
+ {
+ if (this.Api.AppToken.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
=> this.Api.CurrentScreenName;
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)
TwitterStatus[] statuses;
if (more)
{
- statuses = await this.Api.StatusesMentionsTimeline(count, maxId: tab.OldestId)
+ statuses = await this.Api.StatusesMentionsTimeline(count, maxId: tab.OldestId as TwitterStatusId)
.ConfigureAwait(false);
}
else
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);
- // Cookie を使用する場合のみ 99 件を超えて取得するとエラーが返る
- if (this.Api.AppToken.AuthType == APIAuthType.TwitterComCookie)
- count = Math.Min(count, 99);
-
TwitterStatus[] statuses;
- if (MyCommon.IsNullOrEmpty(userName))
+ if (this.Api.AppToken.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.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus())
+ .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, TwitterStatusId id)
{
this.CheckAccountState();
- var status = await this.Api.StatusesShow(id)
- .ConfigureAwait(false);
+ TwitterStatus status;
+ if (this.Api.AppToken.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);
private PostClass CreatePostsFromStatusData(TwitterStatus status, bool favTweet)
=> this.postFactory.CreateFromStatus(status, this.UserId, this.followerId, favTweet);
- private long? CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabModel? tab, bool read)
+ 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 = new TwitterStatusId(status.IdStr);
+ var id = post.StatusId;
if (tab == null)
{
if (TabInformations.GetInstance().ContainsKey(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();
+
+ TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);
- foreach (var status in items.Statuses)
+ 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(new TwitterStatusId(status.IdStr)))
+ if (tab.Contains(post.StatusId))
continue;
}
- var post = this.CreatePostsFromStatusData(status);
-
post.IsRead = read;
if ((post.IsMe && !read) && this.ReadOwnPost) post.IsRead = true;
var request = new ListLatestTweetsTimelineRequest(tab.ListInfo.Id.ToString())
{
Count = count,
- Cursor = more ? tab.CursorBottom : null,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
};
var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
- statuses = response.Tweets.Select(x => x.ToTwitterStatus()).ToArray();
+ var convertedStatuses = response.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus());
+
+ if (!SettingManager.Instance.Common.IsListsIncludeRts)
+ convertedStatuses = convertedStatuses.Where(x => x.RetweetedStatus == null);
+
+ 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>
{
var count = GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, more, false);
- long? maxId = null;
- long? sinceId = null;
- if (more)
+ TwitterStatus[] statuses;
+ if (this.Api.AppToken.AuthType == APIAuthType.TwitterComCookie)
{
- maxId = tab.OldestId - 1;
+ var request = new SearchTimelineRequest(tab.SearchWords)
+ {
+ Count = count,
+ Cursor = more ? tab.CursorBottom : tab.CursorTop,
+ };
+ var response = await request.Send(this.Api.Connection)
+ .ConfigureAwait(false);
+
+ statuses = response.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus())
+ .ToArray();
+
+ 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, DirectMessagesTabModel dmTab, bool backward)
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)