Skip to content

Commit

Permalink
Merge pull request #251 from opentween/user-timeline
Browse files Browse the repository at this point in the history
graphqlエンドポイントを使用したユーザータイムラインの取得に対応
  • Loading branch information
upsilon authored Nov 24, 2023
2 parents 64145ed + 3b20d4c commit dbfce5f
Show file tree
Hide file tree
Showing 17 changed files with 937 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

==== Unreleased
* NEW: graphqlエンドポイントを使用した検索タイムラインの取得に対応
* NEW: graphqlエンドポイントを使用したプロフィール情報の取得に対応
* NEW: graphqlエンドポイントを使用したユーザータイムラインの取得に対応
* CHG: タイムライン更新が停止する不具合が報告される件への暫定的な対処
- タイムライン更新に30秒以上掛かっている場合は完了を待機せず次のタイマーを開始させる
- タイムライン更新の次回実行が1時間以上先になる場合は異常値としてタイマーをリセットする
Expand Down
55 changes: 55 additions & 0 deletions OpenTween.Tests/Api/GraphQL/TwitterGraphqlUserTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// OpenTween - Client of Twitter
// Copyright (c) 2023 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
//
// This file is part of OpenTween.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Xunit;

namespace OpenTween.Api.GraphQL
{
public class TwitterGraphqlUserTest
{
private XElement LoadResponseDocument(string filename)
{
using var stream = File.OpenRead($"Resources/Responses/{filename}");
using var jsonReader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max);
return XElement.Load(jsonReader);
}

[Fact]
public void ToTwitterUser_Test()
{
var userElm = this.LoadResponseDocument("User_Simple.json");
var graphqlUser = new TwitterGraphqlUser(userElm);
var user = graphqlUser.ToTwitterUser();

Assert.Equal("514241801", user.IdStr);
Assert.Equal("opentween", user.ScreenName);
}
}
}
63 changes: 63 additions & 0 deletions OpenTween.Tests/Api/GraphQL/UserByScreenNameRequestTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// OpenTween - Client of Twitter
// Copyright (c) 2023 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
//
// This file is part of OpenTween.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;
using OpenTween.Connection;
using Xunit;

namespace OpenTween.Api.GraphQL
{
public class UserByScreenNameRequestTest
{
[Fact]
public async Task Send_Test()
{
using var responseStream = File.OpenRead("Resources/Responses/UserByScreenName.json");

var mock = new Mock<IApiConnection>();
mock.Setup(x =>
x.GetStreamAsync(It.IsAny<Uri>(), It.IsAny<IDictionary<string, string>>())
)
.Callback<Uri, IDictionary<string, string>>((url, param) =>
{
Assert.Equal(new("https://twitter.com/i/api/graphql/xc8f1g7BYqr6VTzTbvNlGw/UserByScreenName"), url);
Assert.Contains(@"""screen_name"":""opentween""", param["variables"]);
})
.ReturnsAsync(responseStream);

var request = new UserByScreenNameRequest
{
ScreenName = "opentween",
};

var user = await request.Send(mock.Object).ConfigureAwait(false);
Assert.Equal("514241801", user.ToTwitterUser().IdStr);

mock.VerifyAll();
}
}
}
96 changes: 96 additions & 0 deletions OpenTween.Tests/Api/GraphQL/UserTweetsRequestTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// OpenTween - Client of Twitter
// Copyright (c) 2023 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
// All rights reserved.
//
// This file is part of OpenTween.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;
using OpenTween.Connection;
using Xunit;

namespace OpenTween.Api.GraphQL
{
public class UserTweetsRequestTest
{
[Fact]
public async Task Send_Test()
{
using var responseStream = File.OpenRead("Resources/Responses/UserTweets_SimpleTweet.json");

var mock = new Mock<IApiConnection>();
mock.Setup(x =>
x.GetStreamAsync(It.IsAny<Uri>(), It.IsAny<IDictionary<string, string>>())
)
.Callback<Uri, IDictionary<string, string>>((url, param) =>
{
Assert.Equal(new("https://twitter.com/i/api/graphql/2GIWTr7XwadIixZDtyXd4A/UserTweets"), url);
Assert.Equal(3, param.Count);
Assert.Equal("""{"userId":"40480664","count":20,"includePromotedContent":true,"withQuickPromoteEligibilityTweetFields":true,"withVoice":true,"withV2Timeline":true}""", param["variables"]);
Assert.True(param.ContainsKey("features"));
Assert.True(param.ContainsKey("fieldToggles"));
})
.ReturnsAsync(responseStream);

var request = new UserTweetsRequest(userId: "40480664")
{
Count = 20,
};

var response = await request.Send(mock.Object).ConfigureAwait(false);
Assert.Single(response.Tweets);
Assert.Equal("DAABCgABF_tTnZu__-0KAAIWZa6KTRoAAwgAAwAAAAIAAA", response.CursorBottom);

mock.VerifyAll();
}

[Fact]
public async Task Send_RequestCursor_Test()
{
using var responseStream = File.OpenRead("Resources/Responses/UserTweets_SimpleTweet.json");

var mock = new Mock<IApiConnection>();
mock.Setup(x =>
x.GetStreamAsync(It.IsAny<Uri>(), It.IsAny<IDictionary<string, string>>())
)
.Callback<Uri, IDictionary<string, string>>((url, param) =>
{
Assert.Equal(new("https://twitter.com/i/api/graphql/2GIWTr7XwadIixZDtyXd4A/UserTweets"), url);
Assert.Equal(3, param.Count);
Assert.Equal("""{"userId":"40480664","count":20,"includePromotedContent":true,"withQuickPromoteEligibilityTweetFields":true,"withVoice":true,"withV2Timeline":true,"cursor":"aaa"}""", param["variables"]);
Assert.True(param.ContainsKey("features"));
Assert.True(param.ContainsKey("fieldToggles"));
})
.ReturnsAsync(responseStream);

var request = new UserTweetsRequest(userId: "40480664")
{
Count = 20,
Cursor = "aaa",
};

await request.Send(mock.Object).ConfigureAwait(false);
mock.VerifyAll();
}
}
}
9 changes: 9 additions & 0 deletions OpenTween.Tests/OpenTween.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,14 @@
<None Update="Resources\Responses\TimelineTweet_SelfThread.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Responses\User_Simple.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Responses\UserByScreenName.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Responses\UserTweets_SimpleTweet.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
76 changes: 76 additions & 0 deletions OpenTween.Tests/Resources/Responses/UserByScreenName.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"data": {
"user": {
"result": {
"__typename": "User",
"id": "VXNlcjo1MTQyNDE4MDE=",
"rest_id": "514241801",
"affiliates_highlighted_label": {},
"has_graduated_access": false,
"is_blue_verified": false,
"profile_image_shape": "Circle",
"legacy": {
"can_dm": true,
"can_media_tag": false,
"created_at": "Sun Mar 04 11:33:45 +0000 2012",
"default_profile": false,
"default_profile_image": false,
"description": "Windows 用 Twitter クライアント OpenTween のアカウントです。",
"entities": {
"description": {
"urls": []
},
"url": {
"urls": [
{
"display_url": "opentween.org",
"expanded_url": "https://www.opentween.org/",
"url": "https://t.co/An6OJeC28u",
"indices": [
0,
23
]
}
]
}
},
"fast_followers_count": 0,
"favourites_count": 0,
"followers_count": 302,
"friends_count": 1,
"has_custom_timelines": false,
"is_translator": false,
"listed_count": 14,
"location": "",
"media_count": 0,
"name": "OpenTween",
"normal_followers_count": 302,
"pinned_tweet_ids_str": [
"1617124615347908609"
],
"possibly_sensitive": false,
"profile_image_url_https": "https://pbs.twimg.com/profile_images/661168792488153088/-UAFci6G_normal.png",
"profile_interstitial_type": "",
"screen_name": "opentween",
"statuses_count": 31,
"translator_type": "none",
"url": "https://t.co/An6OJeC28u",
"verified": false,
"want_retweets": false,
"withheld_in_countries": []
},
"smart_blocked_by": false,
"smart_blocking": false,
"legacy_extended_profile": {},
"is_profile_translatable": true,
"verification_info": {},
"highlights_info": {
"can_highlight_tweets": false,
"highlighted_tweets": "0"
},
"business_account": {},
"creator_subscriptions_count": 0
}
}
}
}
Loading

0 comments on commit dbfce5f

Please sign in to comment.