// 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;
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)
}
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;
// 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),
+ };
}
}
}