{
var translateApi = new MicrosoftTranslatorApi(http);
- mockHandler.Enqueue(async x =>
+ mockHandler.Enqueue(x =>
{
Assert.Equal(HttpMethod.Post, x.Method);
- Assert.Equal(MicrosoftTranslatorApi.OAuthEndpoint, x.RequestUri);
-
- var body = await x.Content.ReadAsStringAsync()
- .ConfigureAwait(false);
- var query = HttpUtility.ParseQueryString(body);
+ Assert.Equal(MicrosoftTranslatorApi.IssueTokenEndpoint, x.RequestUri);
- Assert.Equal("client_credentials", query["grant_type"]);
- Assert.Equal(ApplicationSettings.AzureClientId, query["client_id"]);
- Assert.Equal(ApplicationSettings.AzureClientSecret, query["client_secret"]);
- Assert.Equal("http://api.microsofttranslator.com", query["scope"]);
+ var keyHeader = x.Headers.First(y => y.Key == "Ocp-Apim-Subscription-Key");
+ Assert.Equal(ApplicationSettings.TranslatorSubscriptionKey, keyHeader.Value.Single());
return new HttpResponseMessage(HttpStatusCode.OK)
{
- Content = new StringContent(@"
-{
- ""access_token"": ""12345acbde"",
- ""token_type"": ""bearer"",
- ""expires_in"": 3600
-}"),
+ Content = new StringContent(@"ACCESS_TOKEN"),
};
});
var result = await translateApi.GetAccessTokenAsync()
.ConfigureAwait(false);
- var expectedToken = (@"12345acbde", TimeSpan.FromSeconds(3600));
+ var expectedToken = (@"ACCESS_TOKEN", TimeSpan.FromMinutes(10));
Assert.Equal(expectedToken, result);
Assert.Equal(0, mockHandler.QueueCount);
}
}
-
- [Fact]
- public void ParseOAuthCredential_ValidResponseTest()
- {
- var jsonBytes = Encoding.UTF8.GetBytes(@"
-{
- ""access_token"": ""12345acbde"",
- ""token_type"": ""bearer"",
- ""expires_in"": 3600
-}
-");
- var expected = (@"12345acbde", TimeSpan.FromSeconds(3600));
- Assert.Equal(expected, MicrosoftTranslatorApi.ParseOAuthCredential(jsonBytes));
- }
-
- [Fact]
- public void ParseOAuthCredential_OmittedExpiresInTest()
- {
- var jsonBytes = Encoding.UTF8.GetBytes(@"
-{
- ""access_token"": ""12345acbde"",
- ""token_type"": ""bearer""
-}
-");
- var expected = (@"12345acbde", TimeSpan.Zero);
- Assert.Equal(expected, MicrosoftTranslatorApi.ParseOAuthCredential(jsonBytes));
- }
}
}
{
public class MicrosoftTranslatorApi
{
- public static readonly Uri OAuthEndpoint = new Uri("https://datamarket.accesscontrol.windows.net/v2/OAuth2-13");
+ public static readonly Uri IssueTokenEndpoint = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
public static readonly Uri TranslateEndpoint = new Uri("https://api.microsofttranslator.com/v2/Http.svc/Translate");
public string AccessToken { get; internal set; }
this.AccessToken = accessToken;
- // expires_in の示す時刻より 30 秒早めに再発行する
+ // アクセストークンの実際の有効期限より 30 秒早めに失効として扱う
this.RefreshAccessTokenAt = DateTime.Now + expiresIn - TimeSpan.FromSeconds(30);
}
internal virtual async Task<(string AccessToken, TimeSpan ExpiresIn)> GetAccessTokenAsync()
{
- var param = new Dictionary<string, string>
+ using (var request = new HttpRequestMessage(HttpMethod.Post, IssueTokenEndpoint))
{
- ["grant_type"] = "client_credentials",
- ["client_id"] = ApplicationSettings.AzureClientId,
- ["client_secret"] = ApplicationSettings.AzureClientSecret,
- ["scope"] = "http://api.microsofttranslator.com",
- };
-
- using (var request = new HttpRequestMessage(HttpMethod.Post, OAuthEndpoint))
- using (var postContent = new FormUrlEncodedContent(param))
- {
- request.Content = postContent;
+ request.Headers.Add("Ocp-Apim-Subscription-Key", ApplicationSettings.TranslatorSubscriptionKey);
using (var response = await this.Http.SendAsync(request).ConfigureAwait(false))
{
- var responseBytes = await response.Content.ReadAsByteArrayAsync()
+ var accessToken = await response.Content.ReadAsStringAsync()
.ConfigureAwait(false);
- return ParseOAuthCredential(responseBytes);
- }
- }
- }
-
- internal static (string AccessToken, TimeSpan ExpiresIn) ParseOAuthCredential(byte[] responseBytes)
- {
- using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(responseBytes, XmlDictionaryReaderQuotas.Max))
- {
- var xElm = XElement.Load(jsonReader);
-
- var tokenTypeElm = xElm.Element("token_type");
- if (tokenTypeElm == null)
- throw new WebApiException("Property `token_type` required");
-
- var accessTokenElm = xElm.Element("access_token");
- if (accessTokenElm == null)
- throw new WebApiException("Property `access_token` required");
-
- var expiresInElm = xElm.Element("expires_in");
-
- int expiresInSeconds;
- if (expiresInElm != null)
- {
- if (!int.TryParse(expiresInElm.Value, out expiresInSeconds))
- throw new WebApiException("Invalid number: expires_in = " + expiresInElm.Value);
- }
- else
- {
- // expires_in が省略された場合は有効期間が不明なので、
- // 次回のリクエスト時は経過時間に関わらずアクセストークンの再発行を行う
- expiresInSeconds = 0;
+ return (accessToken, TimeSpan.FromMinutes(10));
}
-
- return (accessTokenElm.Value, TimeSpan.FromSeconds(expiresInSeconds));
}
}
}
public const string TINAMIApiKey = "4f48bb4858d36";
//=====================================================================
- // Windows Azure Marketplace
- // https://datamarket.azure.com/developer/applications から取得できます。
+ // Microsoft Translator API (Cognitive Service)
+ // https://www.microsoft.com/ja-jp/translator/getstarted.aspx から取得できます。
/// <summary>
- /// Windows Azure Marketplace Client Id
+ /// Translator Text API Subscription Key
/// </summary>
- public readonly static string AzureClientId = "OpenTween";
-
- /// <summary>
- /// Windows Azure Marketplace Client Secret
- /// </summary>
- public readonly static string AzureClientSecret = "UiTaBcOkGxCpjloU/2W3P0fTiHNz+FUeGuzgUz2rwZU=";
+ public readonly static string TranslatorSubscriptionKey = "6c47d2ea341148bf856bdbfafd429db7";
//=====================================================================
// Imgur
==== Ver 1.3.8-dev(2017/xx/xx)
* NEW: bit.ly の認証方式が変更されました
- 短縮URLに bit.ly を使用する場合は、設定画面の「短縮URL」から bit.ly の「認可」ボタンを押して認証情報を入力して下さい
+ * FIX: Microsoft DataMarket廃止により翻訳機能が使用できなくなった不具合を修正
==== Ver 1.3.7(2017/03/20)
* NEW: PNG画像のアップロード時にJPEGへの変換による劣化を回避する機能を追加しました (pic.twitter.com のみ)