OSDN Git Service

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