OSDN Git Service

TimelineTweetの中身が空だった場合は無視する
authorKimura Youichi <kim.upsilon@bucyou.net>
Wed, 24 Jan 2024 02:40:49 +0000 (11:40 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Wed, 24 Jan 2024 02:51:49 +0000 (11:51 +0900)
CHANGELOG.txt
OpenTween.Tests/Api/GraphQL/TimelineTweetTest.cs
OpenTween.Tests/Resources/Responses/TimelineTweet_EmptyTweet.json [new file with mode: 0644]
OpenTween/Api/GraphQL/TimelineResponse.cs
OpenTween/Api/GraphQL/TimelineTweet.cs

index b2de0f8..944406f 100644 (file)
@@ -5,6 +5,7 @@
  * FIX: Cookie使用時にツイート検索の言語指定が効かない不具合を修正
  * FIX: ツイート検索のキーワードを後から変更すると検索結果が表示されない不具合を修正
  * FIX: Cookie使用時にステータスバーにRecentタブのレートリミットが表示されない不具合を修正
+ * FIX: 取得したツイートの中身が空だった場合のエラー処理を改善
 
 ==== Ver 3.12.0(2024/01/20)
  * NEW: graphqlエンドポイントを使用したホームタイムラインの取得に対応
index f195832..a4ccdac 100644 (file)
@@ -188,11 +188,24 @@ namespace OpenTween.Api.GraphQL
             var rootElm = this.LoadResponseDocument("TimelineTweet_TweetTombstone.json");
             var timelineTweet = new TimelineTweet(rootElm);
 
-            Assert.True(timelineTweet.IsTombstone);
+            Assert.False(timelineTweet.IsAvailable);
             var ex = Assert.Throws<WebApiException>(
                 () => timelineTweet.ToTwitterStatus()
             );
             Assert.Equal("This Post is from a suspended account. Learn more", ex.Message);
         }
+
+        [Fact]
+        public void ToStatus_EmptyTweet_Test()
+        {
+            var rootElm = this.LoadResponseDocument("TimelineTweet_EmptyTweet.json");
+            var timelineTweet = new TimelineTweet(rootElm);
+
+            Assert.False(timelineTweet.IsAvailable);
+            var ex = Assert.Throws<WebApiException>(
+                () => timelineTweet.ToTwitterStatus()
+            );
+            Assert.Equal("Tweet is not available", ex.Message);
+        }
     }
 }
diff --git a/OpenTween.Tests/Resources/Responses/TimelineTweet_EmptyTweet.json b/OpenTween.Tests/Resources/Responses/TimelineTweet_EmptyTweet.json
new file mode 100644 (file)
index 0000000..70b9865
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "itemType": "TimelineTweet",
+  "__typename": "TimelineTweet",
+  "tweet_results": {},
+  "tweetDisplayType": "Tweet"
+}
index 052ea61..a2729df 100644 (file)
@@ -34,7 +34,7 @@ namespace OpenTween.Api.GraphQL
     {
         public TwitterStatus[] ToTwitterStatuses()
             => this.Tweets
-                .Where(x => !x.IsTombstone)
+                .Where(x => x.IsAvailable)
                 .Select(x => x.ToTwitterStatus())
                 .ToArray();
     }
index af7b176..e814034 100644 (file)
@@ -23,6 +23,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -38,10 +39,10 @@ namespace OpenTween.Api.GraphQL
 
         public XElement Element { get; }
 
-        public bool IsTombstone
-            => this.tombstoneElm != null;
+        public bool IsAvailable
+            => this.resultElm != null && !this.IsTombstoneResult(this.resultElm);
 
-        private readonly XElement? tombstoneElm;
+        private readonly XElement? resultElm;
 
         public TimelineTweet(XElement element)
         {
@@ -50,19 +51,22 @@ namespace OpenTween.Api.GraphQL
                 throw new ArgumentException($"Invalid itemType: {typeName}", nameof(element));
 
             this.Element = element;
-            this.tombstoneElm = this.TryGetTombstoneElm();
+            this.resultElm = this.TryGetResultElm();
         }
 
-        private XElement? TryGetTombstoneElm()
-            => this.Element.XPathSelectElement("tweet_results/result[__typename[text()='TweetTombstone']]");
+        private XElement? TryGetResultElm()
+            => this.Element.XPathSelectElement("tweet_results/result");
+
+        private bool IsTombstoneResult([NotNullWhen(true)]XElement? resultElm)
+            => resultElm?.Element("__typename")?.Value == "TweetTombstone";
 
         public TwitterStatus ToTwitterStatus()
         {
-            this.ThrowIfTweetIsTombstone();
+            this.ThrowIfTweetIsNotAvailable();
 
             try
             {
-                var resultElm = this.Element.Element("tweet_results")?.Element("result") ?? throw CreateParseError();
+                var resultElm = this.resultElm ?? throw CreateParseError();
                 var status = TimelineTweet.ParseTweetUnion(resultElm);
 
                 if (this.Element.Element("promotedMetadata") != null)
@@ -78,12 +82,15 @@ namespace OpenTween.Api.GraphQL
             }
         }
 
-        public void ThrowIfTweetIsTombstone()
+        public void ThrowIfTweetIsNotAvailable()
         {
-            if (this.tombstoneElm == null)
+            if (this.IsAvailable)
                 return;
 
-            var tombstoneText = this.tombstoneElm.XPathSelectElement("tombstone/text/text")?.Value;
+            string? tombstoneText = null;
+            if (this.IsTombstoneResult(this.resultElm))
+                tombstoneText = this.resultElm.XPathSelectElement("tombstone/text/text")?.Value;
+
             var message = tombstoneText ?? "Tweet is not available";
             var json = JsonUtils.JsonXmlToString(this.Element);