OSDN Git Service

using var を使用する
[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             using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
83
84             using var response = await this.http.SendAsync(request)
85                 .ConfigureAwait(false);
86
87             return await response.Content.ReadAsStringAsync()
88                 .ConfigureAwait(false);
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)
111                 .ConfigureAwait(false);
112             var responseBytes = await response.Content.ReadAsByteArrayAsync()
113                 .ConfigureAwait(false);
114
115             return this.ParseOAuthCredential(responseBytes);
116         }
117
118         private string ParseOAuthCredential(byte[] responseBytes)
119         {
120             using var jsonReader = JsonReaderWriterFactory.CreateJsonReader(responseBytes, XmlDictionaryReaderQuotas.Max);
121             var xElm = XElement.Load(jsonReader);
122
123             var statusCode = xElm.Element("status_code")?.Value ?? "200";
124             if (statusCode != "200")
125             {
126                 var statusText = xElm.Element("status_txt")?.Value;
127                 throw new WebApiException(statusText ?? $"status_code = {statusCode}");
128             }
129
130             var accessToken = xElm.Element("access_token")?.Value;
131             if (accessToken == null)
132                 throw new WebApiException("Property `access_token` required");
133
134             return accessToken;
135         }
136
137         private IEnumerable<KeyValuePair<string, string>> CreateAccessTokenParams()
138         {
139             if (string.IsNullOrEmpty(this.EndUserAccessToken))
140             {
141                 return new[]
142                 {
143                     new KeyValuePair<string, string>("login", this.EndUserLoginName),
144                     new KeyValuePair<string, string>("apiKey", this.EndUserApiKey),
145                 };
146             }
147
148             return new[]
149             {
150                 new KeyValuePair<string, string>("access_token", this.EndUserAccessToken),
151             };
152         }
153     }
154 }