Assert.True(post.IsPromoted);
Assert.Matches(new Regex(@"^\[Promoted\]\n"), post.TextFromApi);
}
+
+ [Fact]
+ public void ToStatus_TweetTombstone_Test()
+ {
+ var rootElm = this.LoadResponseDocument("TimelineTweet_TweetTombstone.json");
+ var timelineTweet = new TimelineTweet(rootElm);
+
+ Assert.True(timelineTweet.IsTombstone);
+ var ex = Assert.Throws<WebApiException>(
+ () => timelineTweet.ToTwitterStatus()
+ );
+ Assert.Equal("This Post is from a suspended account. Learn more", ex.Message);
+ }
}
}
<None Update="Resources\Responses\TimelineTweet_RetweetedTweet.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+ <None Update="Resources\Responses\TimelineTweet_TweetTombstone.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Update="Resources\Responses\TimelineTweet_TweetWithVisibility.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
--- /dev/null
+{
+ "itemType": "TimelineTweet",
+ "__typename": "TimelineTweet",
+ "tweet_results": {
+ "result": {
+ "__typename": "TweetTombstone",
+ "tombstone": {
+ "__typename": "TextTombstone",
+ "text": {
+ "rtl": false,
+ "text": "This Post is from a suspended account. Learn more",
+ "entities": [
+ {
+ "fromIndex": 39,
+ "toIndex": 49,
+ "ref": {
+ "type": "TimelineUrl",
+ "url": "https://help.twitter.com/rules-and-policies/notices-on-twitter",
+ "urlType": "ExternalUrl"
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "tweetDisplayType": "Tweet",
+ "hasModeratedReplies": false
+}
public XElement Element { get; }
+ public bool IsTombstone
+ => this.tombstoneElm != null;
+
+ private readonly XElement? tombstoneElm;
+
public TimelineTweet(XElement element)
{
var typeName = element.Element("itemType")?.Value;
throw new ArgumentException($"Invalid itemType: {typeName}", nameof(element));
this.Element = element;
+ this.tombstoneElm = this.TryGetTombstoneElm();
}
+ private XElement? TryGetTombstoneElm()
+ => this.Element.XPathSelectElement("tweet_results/result[__typename[text()='TweetTombstone']]");
+
public TwitterStatus ToTwitterStatus()
{
+ this.ThrowIfTweetIsTombstone();
+
try
{
var resultElm = this.Element.Element("tweet_results")?.Element("result") ?? throw CreateParseError();
}
}
+ public void ThrowIfTweetIsTombstone()
+ {
+ if (this.tombstoneElm == null)
+ return;
+
+ var tombstoneText = this.tombstoneElm.XPathSelectElement("tombstone/text/text")?.Value;
+ var message = tombstoneText ?? "Tweet is not available";
+ var json = JsonUtils.JsonXmlToString(this.Element);
+
+ throw new WebApiException(message, json);
+ }
+
public static TwitterStatus ParseTweetUnion(XElement tweetUnionElm)
{
var tweetElm = tweetUnionElm.Element("__typename")?.Value switch
var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
- statuses = response.Tweets.Select(x => x.ToTwitterStatus()).ToArray();
+ statuses = response.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus())
+ .ToArray();
+
tab.CursorBottom = response.CursorBottom;
}
else
var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
- var convertedStatuses = response.Tweets.Select(x => x.ToTwitterStatus());
+ var convertedStatuses = response.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus());
+
if (!SettingManager.Instance.Common.IsListsIncludeRts)
convertedStatuses = convertedStatuses.Where(x => x.RetweetedStatus == null);
var response = await request.Send(this.Api.Connection)
.ConfigureAwait(false);
- statuses = response.Tweets.Select(x => x.ToTwitterStatus()).ToArray();
+ statuses = response.Tweets
+ .Where(x => !x.IsTombstone)
+ .Select(x => x.ToTwitterStatus())
+ .ToArray();
+
tab.CursorBottom = response.CursorBottom;
}
else