[DataMember(Name = "created_at")]
public string CreatedAt { get; set; }
+ [DataMember(Name = "display_text_range")]
+ public int[] DisplayTextRange { get; set; }
+
[DataMember(Name = "entities")]
public TwitterEntities Entities { get; set; }
[DataMember(Name = "filter_level")]
public string FilterLevel { get; set; }
+ [DataMember(Name = "full_text")]
+ public string FullText { get; set; }
+
[DataMember(Name = "id")]
public long Id { get; set; }
[DataMember(Name = "source")]
public string Source { get; set; }
- [DataMember(Name = "text")]
- public string Text { get; set; }
-
[DataMember(Name = "truncated")]
public bool Truncated { get; set; }
}
}
+ /// <summary>
+ /// Streaming API または tweet_mode=compat の REST API から返されるツイート (Compatibility mode)
+ /// </summary>
+ [DataContract]
+ public class TwitterStatusCompat
+ {
+ [DataMember(Name = "contributors", IsRequired = false)]
+ public TwitterStatus.Contributor[] Contributors { get; set; } // Nullable
+
+ [DataMember(Name = "coordinates", IsRequired = false)]
+ public GeoJsonPoint Coordinates { get; set; }
+
+ [DataMember(Name = "created_at")]
+ public string CreatedAt { get; set; }
+
+ [DataMember(Name = "entities")]
+ public TwitterEntities Entities { get; set; }
+
+ [DataMember(Name = "extended_entities", IsRequired = false)]
+ public TwitterEntities ExtendedEntities { get; set; }
+
+ [DataMember(Name = "extended_tweet", IsRequired = false)]
+ public TwitterStatusCompat.Extended ExtendedTweet { get; set; }
+
+ [DataContract]
+ public class Extended
+ {
+ [DataMember(Name = "display_text_range")]
+ public int[] DisplayTextRange { get; set; }
+
+ [DataMember(Name = "entities")]
+ public TwitterEntities Entities { get; set; }
+
+ [DataMember(Name = "extended_entities", IsRequired = false)]
+ public TwitterEntities ExtendedEntities { get; set; }
+
+ [DataMember(Name = "full_text")]
+ public string FullText { get; set; }
+ }
+
+ [DataMember(Name = "favorite_count")]
+ public int? FavoriteCount { get; set; }
+
+ [DataMember(Name = "favorited")]
+ public bool? Favorited { get; set; }
+
+ [DataMember(Name = "filter_level")]
+ public string FilterLevel { get; set; }
+
+ [DataMember(Name = "id")]
+ public long Id { get; set; }
+
+ [DataMember(Name = "id_str")]
+ public string IdStr { get; set; }
+
+ [DataMember(Name = "in_reply_to_screen_name")]
+ public string InReplyToScreenName { get; set; } // Nullable
+
+ [DataMember(Name = "in_reply_to_status_id")]
+ public long? InReplyToStatusId { get; set; }
+
+ [DataMember(Name = "in_reply_to_status_id_str")]
+ public string InReplyToStatusIdStr { get; set; } // Nullable
+
+ [DataMember(Name = "in_reply_to_user_id")]
+ public long? InReplyToUserId { get; set; }
+
+ [DataMember(Name = "in_reply_to_user_id_str")]
+ public string InReplyToUserIdStr { get; set; } // Nullable
+
+ [DataMember(Name = "lang")]
+ public string Lang { get; set; } // Nullable
+
+ [DataMember(Name = "place", IsRequired = false)]
+ public TwitterPlace Place { get; set; }
+
+ [DataMember(Name = "possibly_sensitive")]
+ public bool? PossiblySensitive { get; set; }
+
+ [DataMember(Name = "quoted_status_id", IsRequired = false)]
+ public long? QuotedStatusId { get; set; }
+
+ [DataMember(Name = "quoted_status_id_str", IsRequired = false)]
+ public string QuotedStatusIdStr { get; set; }
+
+ [DataMember(Name = "quoted_status", IsRequired = false)]
+ public TwitterStatusCompat QuotedStatus { get; set; }
+
+ [DataMember(Name = "retweet_count")]
+ public int RetweetCount { get; set; }
+
+ [DataMember(Name = "retweeted")]
+ public bool Retweeted { get; set; }
+
+ [DataMember(Name = "retweeted_status", IsRequired = false)]
+ public TwitterStatusCompat RetweetedStatus { get; set; }
+
+ [DataMember(Name = "source")]
+ public string Source { get; set; }
+
+ [DataMember(Name = "text")]
+ public string Text { get; set; }
+
+ [DataMember(Name = "truncated")]
+ public bool Truncated { get; set; }
+
+ [DataMember(Name = "user")]
+ public TwitterUser User { get; set; }
+
+ [DataMember(Name = "withheld_copyright")]
+ public bool WithheldCopyright { get; set; }
+
+ [DataMember(Name = "withheld_in_countries")]
+ public string[] WithheldInCountries { get; set; }
+
+ [DataMember(Name = "withheld_scope")]
+ public string WithheldScope { get; set; }
+
+ /// <summary>Compatibility Modeのツイートを<see cref="TwitterStatus"/>に変換します</summary>
+ public TwitterStatus Normarize()
+ {
+ var normarized = new TwitterStatus
+ {
+ Contributors = this.Contributors,
+ Coordinates = this.Coordinates,
+ CreatedAt = this.CreatedAt,
+ FavoriteCount = this.FavoriteCount,
+ Favorited = this.Favorited,
+ FilterLevel = this.FilterLevel,
+ Id = this.Id,
+ IdStr = this.IdStr,
+ InReplyToScreenName = this.InReplyToScreenName,
+ InReplyToStatusId = this.InReplyToStatusId,
+ InReplyToStatusIdStr = this.InReplyToStatusIdStr,
+ InReplyToUserId = this.InReplyToUserId,
+ InReplyToUserIdStr = this.InReplyToUserIdStr,
+ Lang = this.Lang,
+ Place = this.Place,
+ PossiblySensitive = this.PossiblySensitive,
+ QuotedStatusId = this.QuotedStatusId,
+ QuotedStatusIdStr = this.QuotedStatusIdStr,
+ QuotedStatus = this.QuotedStatus?.Normarize(),
+ RetweetCount = this.RetweetCount,
+ Retweeted = this.Retweeted,
+ RetweetedStatus = this.RetweetedStatus?.Normarize(),
+ Source = this.Source,
+ User = this.User,
+ WithheldCopyright = this.WithheldCopyright,
+ WithheldInCountries = this.WithheldInCountries,
+ WithheldScope = this.WithheldScope,
+ };
+
+ if (this.ExtendedTweet != null)
+ {
+ // Extended Tweet
+ normarized.DisplayTextRange = this.ExtendedTweet.DisplayTextRange;
+ normarized.Entities = this.ExtendedTweet.Entities;
+ normarized.ExtendedEntities = this.ExtendedTweet.ExtendedEntities;
+ normarized.FullText = this.ExtendedTweet.FullText;
+ normarized.Truncated = false;
+ }
+ else
+ {
+ // Classic Tweet
+ normarized.DisplayTextRange = new[] { 0, this.GetCodePointCount(this.Text) };
+ normarized.Entities = this.Entities;
+ normarized.ExtendedEntities = this.ExtendedEntities;
+ normarized.FullText = this.Text;
+ normarized.Truncated = this.Truncated;
+ }
+
+ return normarized;
+ }
+
+ /// <summary>Unicodeコードポイント単位の文字数を返します</summary>
+ private int GetCodePointCount(string text)
+ => text.Length - text.Sum(x => char.IsHighSurrogate(x) ? 1 : 0);
+
+ /// <exception cref="SerializationException"/>
+ public static TwitterStatusCompat ParseJson(string json)
+ {
+ return MyCommon.CreateDataFromJson<TwitterStatusCompat>(json);
+ }
+
+ /// <exception cref="SerializationException"/>
+ public static TwitterStatusCompat[] ParseJsonArray(string json)
+ {
+ return MyCommon.CreateDataFromJson<TwitterStatusCompat[]>(json);
+ }
+ }
+
[DataContract]
public class TwitterDirectMessage
{
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (count != null)
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (count != null)
["include_rts"] = "true",
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (count != null)
["id"] = statusId.ToString(),
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.GetAsync<TwitterStatus>(endpoint, param, "/statuses/show/:id");
["status"] = status,
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (replyToId != null)
["id"] = statusId.ToString(),
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
["result_type"] = "recent",
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (lang != null)
["list_id"] = listId.ToString(),
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (count != null)
["list_id"] = listId.ToString(),
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (cursor != null)
["screen_name"] = screenName,
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/lists/members/show");
["screen_name"] = screenName,
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
["screen_name"] = screenName,
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
["screen_name"] = screenName,
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/users/show/:id");
var param = new Dictionary<string, string>
{
["screen_name"] = screenName,
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (count != null)
var param = new Dictionary<string, string>
{
["id"] = statusId.ToString(),
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
var param = new Dictionary<string, string>
{
["id"] = statusId.ToString(),
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterStatus>(endpoint, param);
var param = new Dictionary<string, string>
{
["screen_name"] = screenName,
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
var param = new Dictionary<string, string>
{
["screen_name"] = screenName,
+ ["tweet_mode"] = "extended",
};
return this.apiConnection.PostLazyAsync<TwitterUser>(endpoint, param);
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
var user = await this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/account/verify_credentials")
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
if (name != null)
{
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
+ ["tweet_mode"] = "extended",
};
var paramMedia = new Dictionary<string, IMediaItem>
{
更新履歴
==== Ver 1.3.5-dev(2016/xx/xx)
+ * NEW: 140文字を越えるツイートの表示に対応しました
* NEW: リスト管理で未取得(グレー表示)の状態が無くなり、常に追加・未追加の状態が表示されるようになりました
* CHG: リストの一覧を表示する際に20件を越える場合のAPIリクエスト回数が少なくなりました
* FIX: UserStreamsが正常に動作しないことがある不具合を修正
//Id
post.RetweetedId = retweeted.Id;
//本文
- post.TextFromApi = retweeted.Text;
+ post.TextFromApi = retweeted.FullText;
entities = retweeted.MergedEntities;
sourceHtml = retweeted.Source;
//Reply先
{
post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
//本文
- post.TextFromApi = status.Text;
+ post.TextFromApi = status.FullText;
entities = status.MergedEntities;
sourceHtml = status.Source;
post.InReplyToStatusId = status.InReplyToStatusId;
var matchStatusUrl = Twitter.StatusUrlRegex.Match(entity.ExpandedUrl);
if (matchStatusUrl.Success && matchStatusUrl.Groups["StatusId"].Value == quoteStatus.IdStr)
{
- var quoteText = this.CreateAccessibleText(quoteStatus.Text, quoteStatus.MergedEntities, quoteStatus: null);
+ var quoteText = this.CreateAccessibleText(quoteStatus.FullText, quoteStatus.MergedEntities, quoteStatus: null);
text = text.Replace(entity.Url, string.Format(Properties.Resources.QuoteStatus_AccessibleText, quoteStatus.User.ScreenName, quoteText));
}
}
{
try
{
- var status = TwitterStatus.ParseJson(line);
- this.CreatePostsFromJson(new[] { status }, MyCommon.WORKERTYPE.UserStream, null, false);
+ var status = TwitterStatusCompat.ParseJson(line);
+ this.CreatePostsFromJson(new[] { status.Normarize() }, MyCommon.WORKERTYPE.UserStream, null, false);
}
catch (SerializationException ex)
{
eventTable.TryGetValue(eventData.Event, out eventType);
evt.Eventtype = eventType;
- TwitterStreamEvent<TwitterStatus> tweetEvent;
+ TwitterStreamEvent<TwitterStatusCompat> tweetEvent;
switch (eventData.Event)
{
return;
case "favorite":
case "unfavorite":
- tweetEvent = TwitterStreamEvent<TwitterStatus>.ParseJson(content);
+ tweetEvent = TwitterStreamEvent<TwitterStatusCompat>.ParseJson(content);
evt.Target = "@" + tweetEvent.TargetObject.User.ScreenName + ":" + WebUtility.HtmlDecode(tweetEvent.TargetObject.Text);
evt.Id = tweetEvent.TargetObject.Id;
case "quoted_tweet":
if (evt.IsMe) return;
- tweetEvent = TwitterStreamEvent<TwitterStatus>.ParseJson(content);
+ tweetEvent = TwitterStreamEvent<TwitterStatusCompat>.ParseJson(content);
evt.Target = "@" + tweetEvent.TargetObject.User.ScreenName + ":" + WebUtility.HtmlDecode(tweetEvent.TargetObject.Text);
evt.Id = tweetEvent.TargetObject.Id;
this.Verified = user.Verified;
if (user.Status != null)
{
- this.RecentPost = user.Status.Text;
+ this.RecentPost = user.Status.FullText;
this.PostCreatedAt = MyCommon.DateTimeParse(user.Status.CreatedAt);
this.PostSource = user.Status.Source;
}
foreach (var entity in entities.Urls)
entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl);
- var html = TweetFormatter.AutoLinkHtml(status.Text, entities);
+ var html = TweetFormatter.AutoLinkHtml(status.FullText, entities);
html = this.mainForm.createDetailHtml(html +
" Posted at " + MyCommon.DateTimeParse(status.CreatedAt) +
" via " + status.Source);