OSDN Git Service

using var を使用する
[opentween/open-tween.git] / OpenTween / Thumbnail / Services / FoursquareCheckin.cs
index 7c2310d..d6827d5 100644 (file)
@@ -20,6 +20,8 @@
 // Boston, MA 02110-1301, USA.
 
 using System;
+using System.Collections.Generic;
+using System.Globalization;
 using System.Net.Http;
 using System.Runtime.Serialization.Json;
 using System.Text.RegularExpressions;
@@ -29,17 +31,25 @@ using System.Web;
 using System.Xml;
 using System.Xml.Linq;
 using System.Xml.XPath;
+using OpenTween.Connection;
+using OpenTween.Models;
 
 namespace OpenTween.Thumbnail.Services
 {
     class FoursquareCheckin : IThumbnailService
     {
         public static readonly Regex UrlPatternRegex =
-            new Regex(@"^https?://foursquare\.com/.+?/checkin/(?<checkin_id>[0-9a-z]+)(?:\?s=(?<signature>[^&]+))?");
+            new Regex(@"^https?://www\.swarmapp\.com/c/(?<checkin_id>[0-9a-zA-Z]+)");
+
+        public static readonly Regex LegacyUrlPatternRegex =
+            new Regex(@"^https?://(?:foursquare\.com|www\.swarmapp\.com)/.+?/checkin/(?<checkin_id>[0-9a-z]+)(?:\?s=(?<signature>[^&]+))?");
 
         public static readonly string ApiBase = "https://api.foursquare.com/v2";
 
-        protected readonly HttpClient http;
+        protected HttpClient http
+            => this.localHttpClient ?? Networking.Http;
+
+        private readonly HttpClient localHttpClient;
 
         public FoursquareCheckin()
             : this(null)
@@ -47,17 +57,83 @@ namespace OpenTween.Thumbnail.Services
         }
 
         public FoursquareCheckin(HttpClient http)
-        {
-            this.http = http ?? MyCommon.CreateHttpClient();
-        }
+            => this.localHttpClient = http;
 
         public override async Task<ThumbnailInfo> GetThumbnailInfoAsync(string url, PostClass post, CancellationToken token)
         {
             // ツイートに位置情報が付与されている場合は何もしない
-            if (post.PostGeo.Lat != 0 || post.PostGeo.Lng != 0)
+            if (post.PostGeo != null)
                 return null;
 
+            var location = await this.FetchCheckinLocation(url, token)
+                .ConfigureAwait(false);
+
+            if (location == null)
+            {
+                location = await this.FetchCheckinLocationLegacy(url, token)
+                    .ConfigureAwait(false);
+            }
+
+            if (location != null)
+            {
+                var map = MapThumb.GetDefaultInstance();
+
+                return await map.GetThumbnailInfoAsync(new PostClass.StatusGeo(location.Longitude, location.Latitude))
+                    .ConfigureAwait(false);
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Foursquare のチェックイン URL から位置情報を取得します
+        /// </summary>
+        public async Task<GlobalLocation> FetchCheckinLocation(string url, CancellationToken token)
+        {
             var match = UrlPatternRegex.Match(url);
+            if (!match.Success)
+                return null;
+
+            var checkinIdGroup = match.Groups["checkin_id"];
+
+            try
+            {
+                // Foursquare のチェックイン情報を取得
+                // 参照: https://developer.foursquare.com/docs/checkins/resolve
+
+                var query = new Dictionary<string, string>
+                {
+                    ["client_id"] = ApplicationSettings.FoursquareClientId,
+                    ["client_secret"] = ApplicationSettings.FoursquareClientSecret,
+                    ["v"] = "20140419", // https://developer.foursquare.com/overview/versioning
+
+                    ["shortId"] = checkinIdGroup.Value,
+                };
+
+                var apiUrl = new Uri(ApiBase + "/checkins/resolve?" + MyCommon.BuildQueryString(query));
+
+                using var response = await this.http.GetAsync(apiUrl, token)
+                    .ConfigureAwait(false);
+
+                response.EnsureSuccessStatusCode();
+
+                var jsonBytes = await response.Content.ReadAsByteArrayAsync()
+                    .ConfigureAwait(false);
+
+                return ParseIntoLocation(jsonBytes);
+            }
+            catch (HttpRequestException)
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// Foursquare のチェックイン URL から位置情報を取得します (古い形式の URL)
+        /// </summary>
+        public async Task<GlobalLocation> FetchCheckinLocationLegacy(string url, CancellationToken token)
+        {
+            var match = LegacyUrlPatternRegex.Match(url);
 
             if (!match.Success)
                 return null;
@@ -70,65 +146,55 @@ namespace OpenTween.Thumbnail.Services
                 // Foursquare のベニュー情報を取得
                 // 参照: https://developer.foursquare.com/docs/venues/venues
 
-                var query = HttpUtility.ParseQueryString(string.Empty);
-                query["client_id"] = ApplicationSettings.FoursquareClientId;
-                query["client_secret"] = ApplicationSettings.FoursquareClientSecret;
-                query["v"] = "20140419"; // https://developer.foursquare.com/overview/versioning
+                var query = new Dictionary<string, string>
+                {
+                    ["client_id"] = ApplicationSettings.FoursquareClientId,
+                    ["client_secret"] = ApplicationSettings.FoursquareClientSecret,
+                    ["v"] = "20140419", // https://developer.foursquare.com/overview/versioning
+                };
 
                 if (signatureGroup.Success)
                     query["signature"] = signatureGroup.Value;
 
-                var apiUrl = new Uri(ApiBase + "/checkins/" + checkinIdGroup.Value + "?" + query);
+                var apiUrl = new Uri(ApiBase + "/checkins/" + checkinIdGroup.Value + "?" + MyCommon.BuildQueryString(query));
 
-                using (var response = await this.http.GetAsync(apiUrl, token).ConfigureAwait(false))
-                {
-                    response.EnsureSuccessStatusCode();
+                using var response = await this.http.GetAsync(apiUrl, token)
+                    .ConfigureAwait(false);
 
-                    var jsonBytes = await response.Content.ReadAsByteArrayAsync()
-                        .ConfigureAwait(false);
+                response.EnsureSuccessStatusCode();
 
-                    var location = ParseIntoLocation(jsonBytes);
-                    if (location == null)
-                        return null;
+                var jsonBytes = await response.Content.ReadAsByteArrayAsync()
+                    .ConfigureAwait(false);
 
-                    var map = MapThumb.GetDefaultInstance();
-
-                    return new ThumbnailInfo
-                    {
-                        ImageUrl = map.CreateMapLinkUrl(location.Latitude, location.Longitude),
-                        ThumbnailUrl = map.CreateStaticMapUrl(location.Latitude, location.Longitude),
-                        TooltipText = null,
-                    };
-                }
+                return ParseIntoLocation(jsonBytes);
+            }
+            catch (HttpRequestException)
+            {
+                return null;
             }
-            catch (HttpRequestException) { }
-
-            return null;
         }
 
         internal static GlobalLocation ParseIntoLocation(byte[] jsonBytes)
         {
-            using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(jsonBytes, XmlDictionaryReaderQuotas.Max))
-            {
-                var xElm = XElement.Load(jsonReader);
+            using var jsonReader = JsonReaderWriterFactory.CreateJsonReader(jsonBytes, XmlDictionaryReaderQuotas.Max);
+            var xElm = XElement.Load(jsonReader);
 
-                var locationElm = xElm.XPathSelectElement("/response/checkin/venue/location");
+            var locationElm = xElm.XPathSelectElement("/response/checkin/venue/location");
 
-                // 座標が得られなかった場合
-                if (locationElm == null)
-                    return null;
+            // 座標が得られなかった場合
+            if (locationElm == null)
+                return null;
 
-                // 月など、地球以外の星の座標である場合
-                var planetElm = locationElm.Element("planet");
-                if (planetElm != null && planetElm.Value != "earth")
-                    return null;
+            // 月など、地球以外の星の座標である場合
+            var planetElm = locationElm.Element("planet");
+            if (planetElm != null && planetElm.Value != "earth")
+                return null;
 
-                return new GlobalLocation
-                {
-                    Latitude = double.Parse(locationElm.Element("lat").Value),
-                    Longitude = double.Parse(locationElm.Element("lng").Value),
-                };
-            }
+            return new GlobalLocation
+            {
+                Latitude = double.Parse(locationElm.Element("lat").Value, CultureInfo.InvariantCulture),
+                Longitude = double.Parse(locationElm.Element("lng").Value, CultureInfo.InvariantCulture),
+            };
         }
     }
 }