Skip to content

Commit

Permalink
Merge pull request #263 from opentween/fix-entity-without-display-url
Browse files Browse the repository at this point in the history
display_url, expanded_url の不足したEntityでエラーになる不具合を修正
  • Loading branch information
upsilon authored Dec 2, 2023
2 parents ce72f69 + 9353251 commit 8058ad6
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* NEW: graphqlエンドポイントに対するレートリミットの表示に対応
* CHG: タイムライン更新時に全件ではなく新着投稿のみ差分を取得する動作に変更
* FIX: 設定したタイムアウト時間を超えてAPI接続が持続する場合がある不具合を修正
* FIX: プロフィール情報のURL欄のパースに失敗する場合がある不具合を修正
- この問題が起きるユーザーのツイートが含まれているとタイムラインの読み込みに失敗する問題も改善されます

==== Ver 3.8.0(2023/11/29)
* NEW: graphqlエンドポイントを使用した検索タイムラインの取得に対応
Expand Down
15 changes: 15 additions & 0 deletions OpenTween.Tests/Api/GraphQL/TwitterGraphqlUserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,20 @@ public void ToTwitterUser_Test()
Assert.Equal("514241801", user.IdStr);
Assert.Equal("opentween", user.ScreenName);
}

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

Assert.Equal("4104111", user.IdStr);
var urlEntity = user.Entities?.Url?.Urls.First()!;
Assert.Equal("http://earthquake.transrain.net/", urlEntity.Url);
Assert.Equal(new[] { 0, 32 }, urlEntity.Indices);
Assert.Null(urlEntity.DisplayUrl);
Assert.Null(urlEntity.ExpandedUrl);
}
}
}
3 changes: 3 additions & 0 deletions OpenTween.Tests/OpenTween.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
<None Update="Resources\Responses\UserByScreenName_Suspended.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Responses\User_EntityWithoutDisplayUrl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Responses\User_Simple.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
125 changes: 125 additions & 0 deletions OpenTween.Tests/Resources/Responses/User_EntityWithoutDisplayUrl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"__typename": "User",
"id": "VXNlcjo0MTA0MTEx",
"rest_id": "4104111",
"affiliates_highlighted_label": {
"label": {
"badge": {
"url": "https://pbs.twimg.com/semantic_core_img/1428827730364096519/4ZXpTBhS?format=png&name=orig"
},
"description": "Automated",
"longDescription": {
"text": "Automated by @ariela",
"entities": [
{
"fromIndex": 13,
"toIndex": 20,
"ref": {
"type": "TimelineRichTextMention",
"screen_name": "ariela",
"mention_results": {
"result": {
"__typename": "User",
"legacy": {
"screen_name": "ariela"
},
"rest_id": "3486871"
}
}
}
}
]
},
"userLabelType": "AutomatedLabel"
}
},
"has_graduated_access": true,
"is_blue_verified": true,
"profile_image_shape": "Circle",
"legacy": {
"can_dm": false,
"can_media_tag": false,
"created_at": "Wed Apr 11 01:33:52 +0000 2007",
"default_profile": false,
"default_profile_image": false,
"description": "警戒:震度1以上 もしくは M3以上の地震情報を提供しています。 基本的に返事は行いません。問い合わせは@ariela もしくはyuki at https://t.co/DrMBNu9mAfにどうぞ。 非公式RTを繰り返すBOTはブロックします。",
"entities": {
"description": {
"urls": [
{
"display_url": "transrain.net",
"expanded_url": "http://transrain.net",
"url": "https://t.co/DrMBNu9mAf",
"indices": [
72,
95
]
}
]
},
"url": {
"urls": [
{
"url": "http://earthquake.transrain.net/",
"indices": [
0,
32
]
}
]
}
},
"fast_followers_count": 0,
"favourites_count": 1,
"followers_count": 3219441,
"friends_count": 5,
"has_custom_timelines": false,
"is_translator": false,
"listed_count": 44208,
"location": "",
"media_count": 0,
"name": "地震速報",
"normal_followers_count": 3219441,
"pinned_tweet_ids_str": [
"1623494931666046977"
],
"possibly_sensitive": false,
"profile_image_url_https": "https://pbs.twimg.com/profile_images/368358807/eqjp_normal.png",
"profile_interstitial_type": "",
"screen_name": "earthquake_jp",
"statuses_count": 59090,
"translator_type": "none",
"url": "http://earthquake.transrain.net/",
"verified": false,
"want_retweets": false,
"withheld_in_countries": []
},
"smart_blocked_by": false,
"smart_blocking": false,
"legacy_extended_profile": {},
"is_profile_translatable": true,
"verification_info": {
"reason": {
"description": {
"text": "This account is verified. Learn more",
"entities": [
{
"from_index": 26,
"to_index": 36,
"ref": {
"url": "https://help.twitter.com/managing-your-account/about-twitter-verified-accounts",
"url_type": "ExternalUrl"
}
}
]
},
"verified_since_msec": "1682244679134"
}
},
"highlights_info": {
"can_highlight_tweets": true,
"highlighted_tweets": "0"
},
"business_account": {},
"creator_subscriptions_count": 0
}
8 changes: 4 additions & 4 deletions OpenTween/Api/DataModel/TwitterEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ public class TwitterEntitySymbol : TwitterEntity
[DataContract]
public class TwitterEntityUrl : TwitterEntity
{
[DataMember(Name = "display_url")]
public string DisplayUrl { get; set; }
[DataMember(Name = "display_url", IsRequired = false)]
public string? DisplayUrl { get; set; }

[DataMember(Name = "expanded_url")]
public string ExpandedUrl { get; set; }
[DataMember(Name = "expanded_url", IsRequired = false)]
public string? ExpandedUrl { get; set; }

[DataMember(Name = "url")]
public string Url { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions OpenTween/Api/GraphQL/TimelineTweet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ static string GetText(XElement elm, string name)
.Select(x => new TwitterEntityUrl()
{
Indices = x.XPathSelectElements("indices/item").Select(x => int.Parse(x.Value)).ToArray(),
DisplayUrl = GetText(x, "display_url"),
ExpandedUrl = GetText(x, "expanded_url"),
DisplayUrl = GetTextOrNull(x, "display_url"),
ExpandedUrl = GetTextOrNull(x, "expanded_url"),
Url = GetText(x, "url"),
})
.ToArray(),
Expand Down
8 changes: 4 additions & 4 deletions OpenTween/Api/GraphQL/TwitterGraphqlUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ static string GetText(XElement elm, string name)
.Select(x => new TwitterEntityUrl()
{
Indices = x.XPathSelectElements("indices/item").Select(x => int.Parse(x.Value)).ToArray(),
DisplayUrl = GetText(x, "display_url"),
ExpandedUrl = GetText(x, "expanded_url"),
DisplayUrl = GetTextOrNull(x, "display_url"),
ExpandedUrl = GetTextOrNull(x, "expanded_url"),
Url = GetText(x, "url"),
})
.ToArray(),
Expand All @@ -108,8 +108,8 @@ static string GetText(XElement elm, string name)
.Select(x => new TwitterEntityUrl()
{
Indices = x.XPathSelectElements("indices/item").Select(x => int.Parse(x.Value)).ToArray(),
DisplayUrl = GetText(x, "display_url"),
ExpandedUrl = GetText(x, "expanded_url"),
DisplayUrl = GetTextOrNull(x, "display_url"),
ExpandedUrl = GetTextOrNull(x, "expanded_url"),
Url = GetText(x, "url"),
})
.ToArray(),
Expand Down
6 changes: 3 additions & 3 deletions OpenTween/Models/TwitterPostFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public PostClass CreateFromStatus(
.ToArray();

var expandedUrls = entities.OfType<TwitterEntityUrl>()
.Select(x => new PostClass.ExpandedUrlInfo(x.Url, x.ExpandedUrl))
.Select(x => new PostClass.ExpandedUrlInfo(x.Url, x.ExpandedUrl ?? x.Url))
.ToArray();

// メモリ使用量削減 (同一のテキストであれば同一の string インスタンスを参照させる)
Expand Down Expand Up @@ -229,7 +229,7 @@ long selfUserId
.ToArray();

var expandedUrls = entities.OfType<TwitterEntityUrl>()
.Select(x => new PostClass.ExpandedUrlInfo(x.Url, x.ExpandedUrl))
.Select(x => new PostClass.ExpandedUrlInfo(x.Url, x.ExpandedUrl ?? x.Url))
.ToArray();

// 以下、ユーザー情報
Expand Down Expand Up @@ -512,7 +512,7 @@ public static IEnumerable<TwitterStatusId> GetQuoteTweetStatusIds(IEnumerable<Tw
{
entities ??= Enumerable.Empty<TwitterEntity>();

var urls = entities.OfType<TwitterEntityUrl>().Select(x => x.ExpandedUrl);
var urls = entities.OfType<TwitterEntityUrl>().Select(x => x.ExpandedUrl ?? x.Url);

if (quotedStatusLink != null)
urls = urls.Append(quotedStatusLink.Expanded);
Expand Down
2 changes: 1 addition & 1 deletion OpenTween/TweetFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private static string FormatUrlEntity(string targetText, TwitterEntityUrl entity

// 過去に存在した壊れたエンティティの対策
// 参照: https://dev.twitter.com/discussions/12628
if (entity.DisplayUrl == null)
if (entity.DisplayUrl == null || entity.ExpandedUrl == null)
{
expandedUrl = MyCommon.ConvertToReadableUrl(targetText);
return $"""<a href="{E(entity.Url)}" title="{E(expandedUrl)}">{T(E(targetText))}</a>""";
Expand Down
7 changes: 6 additions & 1 deletion OpenTween/UserInfoDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,12 @@ private async Task SetDescriptionAsync(string? descriptionText, TwitterEntities?
var urlEntities = entities?.Urls ?? Array.Empty<TwitterEntityUrl>();

foreach (var entity in urlEntities)
{
if (entity.ExpandedUrl == null)
continue;

entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl);
}

// user.entities には urls 以外のエンティティが含まれていないため、テキストをもとに生成する
var mergedEntities = urlEntities.AsEnumerable<TwitterEntity>()
Expand Down Expand Up @@ -253,7 +258,7 @@ private async Task SetRecentStatusAsync(TwitterStatus? status, CancellationToken
var urlEntities = entities.Urls ?? Array.Empty<TwitterEntityUrl>();

foreach (var entity in urlEntities)
entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl);
entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl ?? entity.Url);

var mergedEntities = entities.Concat(TweetExtractor.ExtractEmojiEntities(status.FullText));

Expand Down

0 comments on commit 8058ad6

Please sign in to comment.