Skip to content

Commit

Permalink
Merge pull request #247 from opentween/adjust-promotion-sorting
Browse files Browse the repository at this point in the history
Promoted Tweetのソート順をAPIレスポンスの順序に合わせる
  • Loading branch information
upsilon authored Nov 20, 2023
2 parents bc595bd + b11182f commit b51c625
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 67 deletions.
10 changes: 5 additions & 5 deletions OpenTween.Tests/Api/TwitterApiTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public async Task StatusesHomeTimeline_Test()
using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret"));
twitterApi.ApiConnection = mock.Object;

await twitterApi.StatusesHomeTimeline(200, maxId: 900L, sinceId: 100L)
await twitterApi.StatusesHomeTimeline(200, maxId: new("900"), sinceId: new("100"))
.ConfigureAwait(false);

mock.VerifyAll();
Expand Down Expand Up @@ -133,7 +133,7 @@ public async Task StatusesMentionsTimeline_Test()
using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret"));
twitterApi.ApiConnection = mock.Object;

await twitterApi.StatusesMentionsTimeline(200, maxId: 900L, sinceId: 100L)
await twitterApi.StatusesMentionsTimeline(200, maxId: new("900"), sinceId: new("100"))
.ConfigureAwait(false);

mock.VerifyAll();
Expand Down Expand Up @@ -164,7 +164,7 @@ public async Task StatusesUserTimeline_Test()
using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret"));
twitterApi.ApiConnection = mock.Object;

await twitterApi.StatusesUserTimeline("twitterapi", count: 200, maxId: 900L, sinceId: 100L)
await twitterApi.StatusesUserTimeline("twitterapi", count: 200, maxId: new("900"), sinceId: new("100"))
.ConfigureAwait(false);

mock.VerifyAll();
Expand Down Expand Up @@ -366,7 +366,7 @@ public async Task SearchTweets_Test()
using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret"));
twitterApi.ApiConnection = mock.Object;

await twitterApi.SearchTweets("from:twitterapi", "en", count: 200, maxId: 900L, sinceId: 100L)
await twitterApi.SearchTweets("from:twitterapi", "en", count: 200, maxId: new("900"), sinceId: new("100"))
.ConfigureAwait(false);

mock.VerifyAll();
Expand Down Expand Up @@ -553,7 +553,7 @@ public async Task ListsStatuses_Test()
using var twitterApi = new TwitterApi(ApiKey.Create("fake_consumer_key"), ApiKey.Create("fake_consumer_secret"));
twitterApi.ApiConnection = mock.Object;

await twitterApi.ListsStatuses(12345L, count: 200, maxId: 900L, sinceId: 100L, includeRTs: true)
await twitterApi.ListsStatuses(12345L, count: 200, maxId: new("900"), sinceId: new("100"), includeRTs: true)
.ConfigureAwait(false);

mock.VerifyAll();
Expand Down
4 changes: 2 additions & 2 deletions OpenTween.Tests/Api/TwitterV2/GetTimelineRequestTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public async Task StatusesMentionsTimeline_Test()
var request = new GetTimelineRequest(userId: 100L)
{
MaxResults = 200,
SinceId = "100",
UntilId = "900",
SinceId = new("100"),
UntilId = new("900"),
};

await request.Send(mock.Object).ConfigureAwait(false);
Expand Down
34 changes: 34 additions & 0 deletions OpenTween.Tests/Models/PostIdTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,40 @@ public void CompareTo_SameIdTypeTest()
Assert.Equal(0, a.CompareTo(a));
}

[Fact]
public void OperatorGreaterThan_Test()
{
var a = this.CreatePostId("twitter", "100");
var b = this.CreatePostId("twitter", "200");
#pragma warning disable CS1718
Assert.False(a < a);
Assert.True(a < b);
Assert.False(b < a);
Assert.False(b < b);
Assert.True(a <= a);
Assert.True(a <= b);
Assert.False(b <= a);
Assert.True(b <= b);
#pragma warning restore CS1718
}

[Fact]
public void OperatorLessThan_Test()
{
var a = this.CreatePostId("twitter", "100");
var b = this.CreatePostId("twitter", "200");
#pragma warning disable CS1718
Assert.False(a > a);
Assert.False(a > b);
Assert.True(b > a);
Assert.False(b > b);
Assert.True(a >= a);
Assert.False(a >= b);
Assert.True(b >= a);
Assert.True(b >= b);
#pragma warning restore CS1718
}

[Fact]
public void Equals_Test()
{
Expand Down
135 changes: 135 additions & 0 deletions OpenTween.Tests/Models/TwitterPostFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -654,5 +654,140 @@ public void ParseDateTimeFromSnowflakeId_FallbackTest()
var expected = new DateTimeUtc(2006, 3, 21, 20, 50, 14, 0);
Assert.Equal(expected, TwitterPostFactory.ParseDateTimeFromSnowflakeId(statusId, createdAtStr));
}

[Fact]
public void AdjustSortKeyForPromotedPost_BetweenNormatPostsTest()
{
// プロモーション以外のツイートは投稿日時の降順に並んでいると仮定する
var posts = new[]
{
new PostClass
{
IsPromoted = false,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 6),
},
// 中間にあるプロモーションツイートは前後のツイートの中間に配置されるように調整する
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 7, 31, 0, 0, 0, 0),
},
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 9, 22, 0, 0, 0, 0),
},
new PostClass
{
IsPromoted = false,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 0),
},
};

TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);

Assert.Equal(new(2023, 8, 1, 0, 0, 0, 6), posts[0].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 4), posts[1].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 2), posts[2].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 0), posts[3].CreatedAtForSorting);
}

[Fact]
public void AdjustSortKeyForPromotedPost_BeforeNormatPostsTest()
{
// プロモーション以外のツイートは投稿日時の降順に並んでいると仮定する
var posts = new[]
{
// 先頭にあるプロモーションツイートは最初の通常ツイートを基準に 1ms ずつ加算した値をセットする
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 7, 31, 0, 0, 0, 0),
},
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 9, 22, 0, 0, 0, 0),
},
new PostClass
{
IsPromoted = false,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 0),
},
};

TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);

Assert.Equal(new(2023, 8, 1, 0, 0, 0, 2), posts[0].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 1), posts[1].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 0), posts[2].CreatedAtForSorting);
}

[Fact]
public void AdjustSortKeyForPromotedPost_AfterNormatPostsTest()
{
// プロモーション以外のツイートは投稿日時の降順に並んでいると仮定する
var posts = new[]
{
new PostClass
{
IsPromoted = false,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 6),
},
// 末尾にあるプロモーションツイートは最後の通常ツイートを基準に 1ms ずつ減算した値をセットする
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 7, 31, 0, 0, 0, 0),
},
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 9, 22, 0, 0, 0, 0),
},
};

TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);

Assert.Equal(new(2023, 8, 1, 0, 0, 0, 6), posts[0].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 5), posts[1].CreatedAtForSorting);
Assert.Equal(new(2023, 8, 1, 0, 0, 0, 4), posts[2].CreatedAtForSorting);
}

[Fact]
public void AdjustSortKeyForPromotedPost_NormatPostsOnlyTest()
{
// プロモーションツイートが一件もない場合も正常に動くことをテストする
var posts = new[]
{
new PostClass
{
IsPromoted = false,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 0),
},
};

TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);

Assert.Equal(new(2023, 8, 1, 0, 0, 0, 0), posts[0].CreatedAtForSorting);
}

[Fact]
public void AdjustSortKeyForPromotedPost_PromotedPostsOnlyTest()
{
// プロモーションツイートしか存在しない場合も正常に動くことをテストする
var posts = new[]
{
new PostClass
{
IsPromoted = true,
CreatedAtForSorting = new(2023, 8, 1, 0, 0, 0, 0),
},
};

TwitterPostFactory.AdjustSortKeyForPromotedPost(posts);

Assert.Equal(new(2023, 8, 1, 0, 0, 0, 0), posts[0].CreatedAtForSorting);
}
}
}
30 changes: 15 additions & 15 deletions OpenTween/Api/TwitterApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void Initialize(TwitterAppToken appToken, string accessToken, string acce
this.CurrentScreenName = screenName;
}

public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, long? maxId = null, long? sinceId = null)
public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
{
var endpoint = new Uri("statuses/home_timeline.json", UriKind.Relative);
var param = new Dictionary<string, string>
Expand All @@ -88,14 +88,14 @@ public Task<TwitterStatus[]> StatusesHomeTimeline(int? count = null, long? maxId
if (count != null)
param["count"] = count.ToString();
if (maxId != null)
param["max_id"] = maxId.ToString();
param["max_id"] = maxId.Id;
if (sinceId != null)
param["since_id"] = sinceId.ToString();
param["since_id"] = sinceId.Id;

return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/home_timeline");
}

public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, long? maxId = null, long? sinceId = null)
public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
{
var endpoint = new Uri("statuses/mentions_timeline.json", UriKind.Relative);
var param = new Dictionary<string, string>
Expand All @@ -108,14 +108,14 @@ public Task<TwitterStatus[]> StatusesMentionsTimeline(int? count = null, long? m
if (count != null)
param["count"] = count.ToString();
if (maxId != null)
param["max_id"] = maxId.ToString();
param["max_id"] = maxId.Id;
if (sinceId != null)
param["since_id"] = sinceId.ToString();
param["since_id"] = sinceId.Id;

return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/mentions_timeline");
}

public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, long? maxId = null, long? sinceId = null)
public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
{
var endpoint = new Uri("statuses/user_timeline.json", UriKind.Relative);
var param = new Dictionary<string, string>
Expand All @@ -130,9 +130,9 @@ public Task<TwitterStatus[]> StatusesUserTimeline(string screenName, int? count
if (count != null)
param["count"] = count.ToString();
if (maxId != null)
param["max_id"] = maxId.ToString();
param["max_id"] = maxId.Id;
if (sinceId != null)
param["since_id"] = sinceId.ToString();
param["since_id"] = sinceId.Id;

return this.Connection.GetAsync<TwitterStatus[]>(endpoint, param, "/statuses/user_timeline");
}
Expand Down Expand Up @@ -221,7 +221,7 @@ public Task<LazyJson<TwitterStatus>> StatusesRetweet(TwitterStatusId statusId)
return this.Connection.PostLazyAsync<TwitterStatus>(endpoint, param);
}

public Task<TwitterSearchResult> SearchTweets(string query, string? lang = null, int? count = null, long? maxId = null, long? sinceId = null)
public Task<TwitterSearchResult> SearchTweets(string query, string? lang = null, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null)
{
var endpoint = new Uri("search/tweets.json", UriKind.Relative);
var param = new Dictionary<string, string>
Expand All @@ -238,9 +238,9 @@ public Task<TwitterSearchResult> SearchTweets(string query, string? lang = null,
if (count != null)
param["count"] = count.ToString();
if (maxId != null)
param["max_id"] = maxId.ToString();
param["max_id"] = maxId.Id;
if (sinceId != null)
param["since_id"] = sinceId.ToString();
param["since_id"] = sinceId.Id;

return this.Connection.GetAsync<TwitterSearchResult>(endpoint, param, "/search/tweets");
}
Expand Down Expand Up @@ -340,7 +340,7 @@ public Task<LazyJson<TwitterList>> ListsDestroy(long listId)
return this.Connection.PostLazyAsync<TwitterList>(endpoint, param);
}

public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, long? maxId = null, long? sinceId = null, bool? includeRTs = null)
public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, TwitterStatusId? maxId = null, TwitterStatusId? sinceId = null, bool? includeRTs = null)
{
var endpoint = new Uri("lists/statuses.json", UriKind.Relative);
var param = new Dictionary<string, string>
Expand All @@ -354,9 +354,9 @@ public Task<TwitterStatus[]> ListsStatuses(long listId, int? count = null, long?
if (count != null)
param["count"] = count.ToString();
if (maxId != null)
param["max_id"] = maxId.ToString();
param["max_id"] = maxId.Id;
if (sinceId != null)
param["since_id"] = sinceId.ToString();
param["since_id"] = sinceId.Id;
if (includeRTs != null)
param["include_rts"] = includeRTs.Value ? "true" : "false";

Expand Down
9 changes: 5 additions & 4 deletions OpenTween/Api/TwitterV2/GetTimelineRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using System.Threading.Tasks;
using OpenTween.Api.DataModel;
using OpenTween.Connection;
using OpenTween.Models;

namespace OpenTween.Api.TwitterV2
{
Expand All @@ -39,9 +40,9 @@ public class GetTimelineRequest

public int? MaxResults { get; set; }

public string? UntilId { get; set; }
public TwitterStatusId? UntilId { get; set; }

public string? SinceId { get; set; }
public TwitterStatusId? SinceId { get; set; }

public GetTimelineRequest(long userId)
=> this.UserId = userId;
Expand All @@ -60,10 +61,10 @@ private Dictionary<string, string> CreateParameters()
param["max_results"] = this.MaxResults.ToString();

if (this.UntilId != null)
param["until_id"] = this.UntilId;
param["until_id"] = this.UntilId.Id;

if (this.SinceId != null)
param["since_id"] = this.SinceId;
param["since_id"] = this.SinceId.Id;

return param;
}
Expand Down
2 changes: 1 addition & 1 deletion OpenTween/Models/HomeTabModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class HomeTabModel : TabModel
public override MyCommon.TabUsageType TabType
=> MyCommon.TabUsageType.Home;

public long OldestId { get; set; } = long.MaxValue;
public PostId? OldestId { get; set; }

public int TweetsPerHour => this.tweetsPerHour;

Expand Down
2 changes: 1 addition & 1 deletion OpenTween/Models/ListTimelineTabModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override MyCommon.TabUsageType TabType

public ListElement ListInfo { get; set; }

public long OldestId { get; set; } = long.MaxValue;
public PostId? OldestId { get; set; }

public string? CursorBottom { get; set; }

Expand Down
2 changes: 1 addition & 1 deletion OpenTween/Models/MentionsTabModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class MentionsTabModel : FilterTabModel
public override MyCommon.TabUsageType TabType
=> MyCommon.TabUsageType.Mentions;

public long OldestId { get; set; } = long.MaxValue;
public PostId? OldestId { get; set; }

public MentionsTabModel()
: this(MyCommon.DEFAULTTAB.REPLY)
Expand Down
Loading

0 comments on commit b51c625

Please sign in to comment.