OSDN Git Service

動作しなくなっていたFoursquareのサムネイル表示を再実装
[opentween/open-tween.git] / OpenTween / Thumbnail / Services / FoursquareCheckin.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2012 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.Net.Http;
24 using System.Runtime.Serialization.Json;
25 using System.Text.RegularExpressions;
26 using System.Threading;
27 using System.Threading.Tasks;
28 using System.Web;
29 using System.Xml;
30 using System.Xml.Linq;
31 using System.Xml.XPath;
32
33 namespace OpenTween.Thumbnail.Services
34 {
35     class FoursquareCheckin : IThumbnailService
36     {
37         public static readonly Regex UrlPatternRegex =
38             new Regex(@"^https?://foursquare\.com/.+?/checkin/(?<checkin_id>[0-9a-z]+)(?:\?s=(?<signature>[^&]+))?");
39
40         public static readonly string ApiBase = "https://api.foursquare.com/v2";
41
42         protected readonly HttpClient http;
43
44         public FoursquareCheckin()
45             : this(null)
46         {
47         }
48
49         public FoursquareCheckin(HttpClient http)
50         {
51             this.http = http ?? MyCommon.CreateHttpClient();
52         }
53
54         public override async Task<ThumbnailInfo> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token)
55         {
56             if (!AppendSettingDialog.Instance.IsPreviewFoursquare)
57                 return null;
58
59             // ツイートに位置情報が付与されている場合は何もしない
60             if (post.PostGeo.Lat != 0 || post.PostGeo.Lng != 0)
61                 return null;
62
63             var match = UrlPatternRegex.Match(url);
64
65             if (!match.Success)
66                 return null;
67
68             var checkinIdGroup = match.Groups["checkin_id"];
69             var signatureGroup = match.Groups["signature"];
70
71             try
72             {
73                 // Foursquare のベニュー情報を取得
74                 // 参照: https://developer.foursquare.com/docs/venues/venues
75
76                 var query = HttpUtility.ParseQueryString(string.Empty);
77                 query["client_id"] = ApplicationSettings.FoursquareClientId;
78                 query["client_secret"] = ApplicationSettings.FoursquareClientSecret;
79                 query["v"] = "20140419"; // https://developer.foursquare.com/overview/versioning
80
81                 if (signatureGroup.Success)
82                     query["signature"] = signatureGroup.Value;
83
84                 var apiUrl = new Uri(ApiBase + "/checkins/" + checkinIdGroup.Value + "?" + query);
85
86                 using (var response = await this.http.GetAsync(apiUrl, token).ConfigureAwait(false))
87                 {
88                     response.EnsureSuccessStatusCode();
89
90                     var jsonBytes = await response.Content.ReadAsByteArrayAsync()
91                         .ConfigureAwait(false);
92
93                     var location = ParseIntoLocation(jsonBytes);
94                     if (location == null)
95                         return null;
96
97                     var map = MapThumb.GetDefaultInstance();
98
99                     return new ThumbnailInfo(this.http)
100                     {
101                         ImageUrl = map.CreateMapLinkUrl(location.Latitude, location.Longitude),
102                         ThumbnailUrl = map.CreateStaticMapUrl(location.Latitude, location.Longitude),
103                         TooltipText = null,
104                     };
105                 }
106             }
107             catch (HttpRequestException) { }
108
109             return null;
110         }
111
112         internal static GlobalLocation ParseIntoLocation(byte[] jsonBytes)
113         {
114             using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(jsonBytes, XmlDictionaryReaderQuotas.Max))
115             {
116                 var xElm = XElement.Load(jsonReader);
117
118                 var locationElm = xElm.XPathSelectElement("/response/checkin/venue/location");
119
120                 // 座標が得られなかった場合
121                 if (locationElm == null)
122                     return null;
123
124                 // 月など、地球以外の星の座標である場合
125                 var planetElm = locationElm.Element("planet");
126                 if (planetElm != null && planetElm.Value != "earth")
127                     return null;
128
129                 return new GlobalLocation
130                 {
131                     Latitude = double.Parse(locationElm.Element("lat").Value),
132                     Longitude = double.Parse(locationElm.Element("lng").Value),
133                 };
134             }
135         }
136     }
137 }