OSDN Git Service

TwitterApi.EscapeJsonStringメソッドをJsonUtilsクラスへ移動
[opentween/open-tween.git] / OpenTween / Api / BitlyApi.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2017 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.Net.Http;
26 using System.Net.Http.Headers;
27 using System.Runtime.Serialization.Json;
28 using System.Text;
29 using System.Text.RegularExpressions;
30 using System.Threading.Tasks;
31 using System.Xml;
32 using System.Xml.Linq;
33 using OpenTween.Connection;
34
35 namespace OpenTween.Api
36 {
37     public class BitlyApi
38     {
39         public static readonly Uri ApiBase = new Uri("https://api-ssl.bitly.com/");
40
41         public string EndUserAccessToken { get; set; }
42
43         public string EndUserLoginName { get; set; }
44         public string EndUserApiKey { get; set; }
45
46         private HttpClient http => this.localHttpClient ?? Networking.Http;
47         private readonly HttpClient localHttpClient;
48
49         public BitlyApi()
50             : this(null)
51         {
52         }
53
54         public BitlyApi(HttpClient http)
55             => this.localHttpClient = http;
56
57         public async Task<Uri> ShortenAsync(Uri srcUri, string domain = null)
58         {
59             var query = new Dictionary<string, string>
60             {
61                 ["format"] = "txt",
62                 ["longUrl"] = srcUri.OriginalString,
63             };
64
65             if (!string.IsNullOrEmpty(domain))
66                 query["domain"] = domain;
67
68             var uri = new Uri("/v3/shorten", UriKind.Relative);
69             var responseText = await this.GetAsync(uri, query).ConfigureAwait(false);
70
71             if (!Regex.IsMatch(responseText, @"^https?://"))
72                 throw new WebApiException("Failed to create URL.", responseText);
73
74             return new Uri(responseText.TrimEnd());
75         }
76
77         public async Task<string> GetAsync(Uri endpoint, IEnumerable<KeyValuePair<string, string>> param)
78         {
79             var paramWithToken = param.Concat(this.CreateAccessTokenParams());
80
81             var requestUri = new Uri(new Uri(ApiBase, endpoint), "?" + MyCommon.BuildQueryString(paramWithToken));
82
83             using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
84             using (var response = await this.http.SendAsync(request).ConfigureAwait(false))
85             {
86                 return await response.Content.ReadAsStringAsync()
87                     .ConfigureAwait(false);
88             }
89         }
90
91         public async Task<string> GetAccessTokenAsync(string username, string password)
92         {
93             var param = new Dictionary<string, string>
94             {
95                 ["grant_type"] = "password",
96                 ["username"] = username,
97                 ["password"] = password,
98             };
99
100             var endpoint = new Uri(ApiBase, "/oauth/access_token");
101
102             using (var request = new HttpRequestMessage(HttpMethod.Post, endpoint))
103             using (var postContent = new FormUrlEncodedContent(param))
104             {
105                 var authzParam = ApplicationSettings.BitlyClientId + ":" + ApplicationSettings.BitlyClientSecret;
106                 request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(authzParam)));
107
108                 request.Content = postContent;
109
110                 using (var response = await this.http.SendAsync(request).ConfigureAwait(false))
111                 {
112                     var responseBytes = await response.Content.ReadAsByteArrayAsync()
113                         .ConfigureAwait(false);
114
115                     return this.ParseOAuthCredential(responseBytes);
116                 }
117             }
118         }
119
120         private string ParseOAuthCredential(byte[] responseBytes)
121         {
122             using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(responseBytes, XmlDictionaryReaderQuotas.Max))
123             {
124                 var xElm = XElement.Load(jsonReader);
125
126                 var statusCode = xElm.Element("status_code")?.Value ?? "200";
127                 if (statusCode != "200")
128                 {
129                     var statusText = xElm.Element("status_txt")?.Value;
130                     throw new WebApiException(statusText ?? $"status_code = {statusCode}");
131                 }
132
133                 var accessToken = xElm.Element("access_token")?.Value;
134                 if (accessToken == null)
135                     throw new WebApiException("Property `access_token` required");
136
137                 return accessToken;
138             }
139         }
140
141         private IEnumerable<KeyValuePair<string, string>> CreateAccessTokenParams()
142         {
143             if (string.IsNullOrEmpty(this.EndUserAccessToken))
144             {
145                 return new[]
146                 {
147                     new KeyValuePair<string, string>("login", this.EndUserLoginName),
148                     new KeyValuePair<string, string>("apiKey", this.EndUserApiKey),
149                 };
150             }
151
152             return new[]
153             {
154                 new KeyValuePair<string, string>("access_token", this.EndUserAccessToken),
155             };
156         }
157     }
158 }