OSDN Git Service

TwitterEntitiesを最新のスキーマに更新
[opentween/open-tween.git] / OpenTween / Api / DataModel / TwitterStatus.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Runtime.Serialization;
26 using System.Text;
27 using System.Threading.Tasks;
28
29 namespace OpenTween.Api.DataModel
30 {
31     // 参照: https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object
32
33     [DataContract]
34     public class TwitterStatus
35     {
36         [DataMember(Name = "coordinates", IsRequired = false)]
37         public GeoJsonPoint Coordinates { get; set; }
38
39         [DataMember(Name = "created_at")]
40         public string CreatedAt { get; set; }
41
42         [DataMember(Name = "display_text_range")]
43         public int[] DisplayTextRange { get; set; }
44
45         [DataMember(Name = "entities")]
46         public TwitterEntities Entities { get; set; }
47
48         [DataMember(Name = "extended_entities", IsRequired = false)]
49         public TwitterEntities ExtendedEntities { get; set; }
50
51         [DataMember(Name = "favorite_count")]
52         public int? FavoriteCount { get; set; }
53
54         [DataMember(Name = "favorited")]
55         public bool? Favorited { get; set; }
56
57         [DataMember(Name = "filter_level")]
58         public string FilterLevel { get; set; }
59
60         [DataMember(Name = "full_text")]
61         public string FullText { get; set; }
62
63         [DataMember(Name = "id")]
64         public long Id { get; set; }
65
66         [DataMember(Name = "id_str")]
67         public string IdStr { get; set; }
68
69         [DataMember(Name = "in_reply_to_screen_name")]
70         public string InReplyToScreenName { get; set; } // Nullable
71
72         [DataMember(Name = "in_reply_to_status_id")]
73         public long? InReplyToStatusId { get; set; }
74
75         [DataMember(Name = "in_reply_to_status_id_str")]
76         public string InReplyToStatusIdStr { get; set; } // Nullable
77
78         [DataMember(Name = "in_reply_to_user_id")]
79         public long? InReplyToUserId { get; set; }
80
81         [DataMember(Name = "in_reply_to_user_id_str")]
82         public string InReplyToUserIdStr { get; set; } // Nullable
83
84         [DataMember(Name = "is_quote_status")]
85         public bool IsQuoteStatus { get; set; }
86
87         [DataMember(Name = "lang")]
88         public string Lang { get; set; } // Nullable
89
90         [DataMember(Name = "place", IsRequired = false)]
91         public TwitterPlace Place { get; set; }
92
93         [DataMember(Name = "possibly_sensitive")]
94         public bool? PossiblySensitive { get; set; }
95
96         [DataMember(Name = "quote_count")]
97         public int? QuoteCount { get; set; }
98
99         [DataMember(Name = "quoted_status_id", IsRequired = false)]
100         public long? QuotedStatusId { get; set; }
101
102         [DataMember(Name = "quoted_status_id_str", IsRequired = false)]
103         public string QuotedStatusIdStr { get; set; }
104
105         [DataMember(Name = "quoted_status", IsRequired = false)]
106         public TwitterStatus QuotedStatus { get; set; }
107
108         [DataMember(Name = "quoted_status_permalink", IsRequired = false)]
109         public TwitterQuotedStatusPermalink QuotedStatusPermalink { get; set; } // https://twittercommunity.com/t/105473
110
111         [DataMember(Name = "reply_count")]
112         public int ReplyCount { get; set; }
113
114         [DataMember(Name = "retweet_count")]
115         public int RetweetCount { get; set; }
116
117         [DataMember(Name = "retweeted")]
118         public bool Retweeted { get; set; }
119
120         [DataMember(Name = "retweeted_status", IsRequired = false)]
121         public TwitterStatus RetweetedStatus { get; set; }
122
123         [DataMember(Name = "source")]
124         public string Source { get; set; }
125
126         [DataMember(Name = "truncated")]
127         public bool Truncated { get; set; }
128
129         [DataMember(Name = "user")]
130         public TwitterUser User { get; set; }
131
132         [DataMember(Name = "withheld_copyright")]
133         public bool WithheldCopyright { get; set; }
134
135         [DataMember(Name = "withheld_in_countries")]
136         public string[] WithheldInCountries { get; set; }
137
138         [DataMember(Name = "withheld_scope")]
139         public string WithheldScope { get; set; }
140
141         /// <summary>
142         /// Entities と ExtendedEntities をマージした状態のエンティティを返します
143         /// </summary>
144         [IgnoreDataMember]
145         public TwitterEntities MergedEntities
146         {
147             get
148             {
149                 if (this.ExtendedEntities == null)
150                     return this.Entities;
151
152                 return new TwitterEntities
153                 {
154                     Hashtags = this.Entities.Hashtags,
155                     Media = this.ExtendedEntities.Media ?? this.Entities.Media,
156                     Symbols = this.Entities.Symbols,
157                     Urls = this.Entities.Urls,
158                     UserMentions = this.Entities.UserMentions,
159                 };
160             }
161         }
162
163         /// <exception cref="SerializationException"/>
164         public static TwitterStatus ParseJson(string json)
165         {
166             return MyCommon.CreateDataFromJson<TwitterStatus>(json);
167         }
168
169         /// <exception cref="SerializationException"/>
170         public static TwitterStatus[] ParseJsonArray(string json)
171         {
172             return MyCommon.CreateDataFromJson<TwitterStatus[]>(json);
173         }
174     }
175
176     [DataContract]
177     public class TwitterQuotedStatusPermalink
178     {
179         [DataMember(Name = "url")]
180         public string Url { get; set; }
181
182         [DataMember(Name = "expanded")]
183         public string Expanded { get; set; }
184
185         [DataMember(Name = "display")]
186         public string Display { get; set; }
187     }
188
189     /// <summary>
190     /// Streaming API または tweet_mode=compat の REST API から返されるツイート (Compatibility mode)
191     /// </summary>
192     [DataContract]
193     public class TwitterStatusCompat
194     {
195         [DataMember(Name = "coordinates", IsRequired = false)]
196         public GeoJsonPoint Coordinates { get; set; }
197
198         [DataMember(Name = "created_at")]
199         public string CreatedAt { get; set; }
200
201         [DataMember(Name = "entities")]
202         public TwitterEntities Entities { get; set; }
203
204         [DataMember(Name = "extended_entities", IsRequired = false)]
205         public TwitterEntities ExtendedEntities { get; set; }
206
207         [DataMember(Name = "extended_tweet", IsRequired = false)]
208         public TwitterStatusCompat.Extended ExtendedTweet { get; set; }
209
210         [DataContract]
211         public class Extended
212         {
213             [DataMember(Name = "display_text_range")]
214             public int[] DisplayTextRange { get; set; }
215
216             [DataMember(Name = "entities")]
217             public TwitterEntities Entities { get; set; }
218
219             [DataMember(Name = "extended_entities", IsRequired = false)]
220             public TwitterEntities ExtendedEntities { get; set; }
221
222             [DataMember(Name = "full_text")]
223             public string FullText { get; set; }
224         }
225
226         [DataMember(Name = "favorite_count")]
227         public int? FavoriteCount { get; set; }
228
229         [DataMember(Name = "favorited")]
230         public bool? Favorited { get; set; }
231
232         [DataMember(Name = "filter_level")]
233         public string FilterLevel { get; set; }
234
235         [DataMember(Name = "id")]
236         public long Id { get; set; }
237
238         [DataMember(Name = "id_str")]
239         public string IdStr { get; set; }
240
241         [DataMember(Name = "in_reply_to_screen_name")]
242         public string InReplyToScreenName { get; set; } // Nullable
243
244         [DataMember(Name = "in_reply_to_status_id")]
245         public long? InReplyToStatusId { get; set; }
246
247         [DataMember(Name = "in_reply_to_status_id_str")]
248         public string InReplyToStatusIdStr { get; set; } // Nullable
249
250         [DataMember(Name = "in_reply_to_user_id")]
251         public long? InReplyToUserId { get; set; }
252
253         [DataMember(Name = "in_reply_to_user_id_str")]
254         public string InReplyToUserIdStr { get; set; } // Nullable
255
256         [DataMember(Name = "is_quote_status")]
257         public bool IsQuoteStatus { get; set; }
258
259         [DataMember(Name = "lang")]
260         public string Lang { get; set; } // Nullable
261
262         [DataMember(Name = "place", IsRequired = false)]
263         public TwitterPlace Place { get; set; }
264
265         [DataMember(Name = "possibly_sensitive")]
266         public bool? PossiblySensitive { get; set; }
267
268         [DataMember(Name = "quote_count")]
269         public int? QuoteCount { get; set; }
270
271         [DataMember(Name = "quoted_status_id", IsRequired = false)]
272         public long? QuotedStatusId { get; set; }
273
274         [DataMember(Name = "quoted_status_id_str", IsRequired = false)]
275         public string QuotedStatusIdStr { get; set; }
276
277         [DataMember(Name = "quoted_status", IsRequired = false)]
278         public TwitterStatusCompat QuotedStatus { get; set; }
279
280         [DataMember(Name = "quoted_status_permalink", IsRequired = false)]
281         public TwitterQuotedStatusPermalink QuotedStatusPermalink { get; set; } // https://twittercommunity.com/t/105473
282
283         [DataMember(Name = "reply_count")]
284         public int ReplyCount { get; set; }
285
286         [DataMember(Name = "retweet_count")]
287         public int RetweetCount { get; set; }
288
289         [DataMember(Name = "retweeted")]
290         public bool Retweeted { get; set; }
291
292         [DataMember(Name = "retweeted_status", IsRequired = false)]
293         public TwitterStatusCompat RetweetedStatus { get; set; }
294
295         [DataMember(Name = "source")]
296         public string Source { get; set; }
297
298         [DataMember(Name = "text")]
299         public string Text { get; set; }
300
301         [DataMember(Name = "truncated")]
302         public bool Truncated { get; set; }
303
304         [DataMember(Name = "user")]
305         public TwitterUser User { get; set; }
306
307         [DataMember(Name = "withheld_copyright")]
308         public bool WithheldCopyright { get; set; }
309
310         [DataMember(Name = "withheld_in_countries")]
311         public string[] WithheldInCountries { get; set; }
312
313         [DataMember(Name = "withheld_scope")]
314         public string WithheldScope { get; set; }
315
316         /// <summary>Compatibility Modeのツイートを<see cref="TwitterStatus"/>に変換します</summary>
317         public TwitterStatus Normalize()
318         {
319             var normalized = new TwitterStatus
320             {
321                 Coordinates = this.Coordinates,
322                 CreatedAt = this.CreatedAt,
323                 FavoriteCount = this.FavoriteCount,
324                 Favorited = this.Favorited,
325                 FilterLevel = this.FilterLevel,
326                 Id = this.Id,
327                 IdStr = this.IdStr,
328                 InReplyToScreenName = this.InReplyToScreenName,
329                 InReplyToStatusId = this.InReplyToStatusId,
330                 InReplyToStatusIdStr = this.InReplyToStatusIdStr,
331                 InReplyToUserId = this.InReplyToUserId,
332                 InReplyToUserIdStr = this.InReplyToUserIdStr,
333                 IsQuoteStatus = this.IsQuoteStatus,
334                 Lang = this.Lang,
335                 Place = this.Place,
336                 PossiblySensitive = this.PossiblySensitive,
337                 QuoteCount = this.QuoteCount,
338                 QuotedStatusId = this.QuotedStatusId,
339                 QuotedStatusIdStr = this.QuotedStatusIdStr,
340                 QuotedStatus = this.QuotedStatus?.Normalize(),
341                 QuotedStatusPermalink = this.QuotedStatusPermalink,
342                 ReplyCount = this.ReplyCount,
343                 RetweetCount = this.RetweetCount,
344                 Retweeted = this.Retweeted,
345                 RetweetedStatus = this.RetweetedStatus?.Normalize(),
346                 Source = this.Source,
347                 User = this.User,
348                 WithheldCopyright = this.WithheldCopyright,
349                 WithheldInCountries = this.WithheldInCountries,
350                 WithheldScope = this.WithheldScope,
351             };
352
353             if (this.ExtendedTweet != null)
354             {
355                 // Extended Tweet
356                 normalized.DisplayTextRange = this.ExtendedTweet.DisplayTextRange;
357                 normalized.Entities = this.ExtendedTweet.Entities;
358                 normalized.ExtendedEntities = this.ExtendedTweet.ExtendedEntities;
359                 normalized.FullText = this.ExtendedTweet.FullText;
360                 normalized.Truncated = false;
361             }
362             else
363             {
364                 // Classic Tweet
365                 normalized.DisplayTextRange = new[] { 0, this.GetCodePointCount(this.Text) };
366                 normalized.Entities = this.Entities;
367                 normalized.ExtendedEntities = this.ExtendedEntities;
368                 normalized.FullText = this.Text;
369                 normalized.Truncated = this.Truncated;
370             }
371
372             return normalized;
373         }
374
375         /// <summary>Unicodeコードポイント単位の文字数を返します</summary>
376         private int GetCodePointCount(string text)
377             => text.Length - text.Sum(x => char.IsHighSurrogate(x) ? 1 : 0);
378
379         /// <exception cref="SerializationException"/>
380         public static TwitterStatusCompat ParseJson(string json)
381         {
382             return MyCommon.CreateDataFromJson<TwitterStatusCompat>(json);
383         }
384
385         /// <exception cref="SerializationException"/>
386         public static TwitterStatusCompat[] ParseJsonArray(string json)
387         {
388             return MyCommon.CreateDataFromJson<TwitterStatusCompat[]>(json);
389         }
390     }
391
392     [DataContract]
393     public class TwitterDirectMessage
394     {
395         [DataMember(Name = "entities", IsRequired = false)]
396         public TwitterEntities Entities { get; set; }
397
398         [DataMember(Name = "created_at")]
399         public string CreatedAt { get; set; }
400
401         [DataMember(Name = "id")]
402         public long Id { get; set; }
403
404         [DataMember(Name = "id_str")]
405         public string IdStr { get; set; }
406
407         [DataMember(Name = "text")]
408         public string Text { get; set; }
409
410         [DataMember(Name = "sender_id")]
411         public long SenderId { get; set; }
412
413         [DataMember(Name = "sender_id_str")]
414         public string SenderIdStr { get; set; }
415
416         [DataMember(Name = "sender_screen_name")]
417         public string SenderScreenName { get; set; }
418
419         [DataMember(Name = "sender", IsRequired = false)]
420         public TwitterUser Sender { get; set; }
421
422         [DataMember(Name = "recipient_id")]
423         public long RecipientId { get; set; }
424
425         [DataMember(Name = "recipient_id_str")]
426         public string RecipientIdStr { get; set; }
427
428         [DataMember(Name = "recipient_screen_name")]
429         public string RecipientScreenName { get; set; }
430
431         [DataMember(Name = "recipient", IsRequired = false)]
432         public TwitterUser Recipient { get; set; }
433
434         /// <exception cref="SerializationException"/>
435         public static TwitterDirectMessage ParseJson(string json)
436         {
437             return MyCommon.CreateDataFromJson<TwitterDirectMessage>(json);
438         }
439
440         /// <exception cref="SerializationException"/>
441         public static TwitterDirectMessage[] ParseJsonArray(string json)
442         {
443             return MyCommon.CreateDataFromJson<TwitterDirectMessage[]>(json);
444         }
445     }
446 }