• R/O
  • HTTP
  • SSH
  • HTTPS

open-tween: Commit

開発に使用するリポジトリ


Commit MetaInfo

Revisiona2eed78b35d5b95cfd641cbf06b04c4f33df0137 (tree)
Time2022-01-22 16:42:39
Authorupsilon <kim.upsilon@bucy...>
CommiterGitHub

Log Message

Merge pull request #87 from opentween/move-upload-api-classes

IMediaUploadService関連のクラスの名前空間を移動

Change Summary

Incremental Difference

--- /dev/null
+++ b/OpenTween.Tests/Api/ImgurApiTest.cs
@@ -0,0 +1,151 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2022 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;
26+using System.Net.Http;
27+using System.Text;
28+using System.Threading.Tasks;
29+using Xunit;
30+
31+namespace OpenTween.Api
32+{
33+ public class ImgurApiTest
34+ {
35+ [Fact]
36+ public async Task UploadFileAsync_Test()
37+ {
38+ using var mockHandler = new HttpMessageHandlerMock();
39+ using var http = new HttpClient(mockHandler);
40+
41+ mockHandler.Enqueue(x =>
42+ {
43+ Assert.Equal(HttpMethod.Post, x.Method);
44+ Assert.Equal(ImgurApi.UploadEndpoint, x.RequestUri);
45+
46+ Assert.Equal("Client-ID", x.Headers.Authorization.Scheme);
47+ Assert.Equal("fake_api_key", x.Headers.Authorization.Parameter);
48+
49+ return new HttpResponseMessage(HttpStatusCode.OK)
50+ {
51+ Content = new StringContent(@"<?xml version=""1.0"" encoding=""utf-8""?>
52+<data type=""array"" success=""1"" status=""200"">
53+ <id>aaaaaaa</id>
54+ <title>てすと</title>
55+ <description/>
56+ <datetime>1234567890</datetime>
57+ <type>image/png</type>
58+ <animated>false</animated>
59+ <width>300</width>
60+ <height>300</height>
61+ <size>1000</size>
62+ <views>0</views>
63+ <bandwidth>0</bandwidth>
64+ <vote/>
65+ <favorite>false</favorite>
66+ <nsfw/>
67+ <section/>
68+ <account_url/>
69+ <account_id>0</account_id>
70+ <is_ad>false</is_ad>
71+ <in_most_viral>false</in_most_viral>
72+ <has_sound>false</has_sound>
73+ <tags/>
74+ <ad_type>0</ad_type>
75+ <ad_url/>
76+ <edited>0</edited>
77+ <in_gallery>false</in_gallery>
78+ <deletehash>aaaaaaaaaaaaaaa</deletehash>
79+ <name/>
80+ <link>https://i.imgur.com/aaaaaaa.png</link>
81+</data>"),
82+ };
83+ });
84+
85+ var imgurApi = new ImgurApi("fake_api_key", http);
86+ using var mediaItem = TestUtils.CreateDummyMediaItem();
87+ var uploadedUrl = await imgurApi.UploadFileAsync(mediaItem, "てすと")
88+ .ConfigureAwait(false);
89+ Assert.Equal("https://i.imgur.com/aaaaaaa.png", uploadedUrl);
90+
91+ Assert.Equal(0, mockHandler.QueueCount);
92+ }
93+
94+ [Fact]
95+ public async Task UploadFileAsync_ErrorResponseTest()
96+ {
97+ using var mockHandler = new HttpMessageHandlerMock();
98+ using var http = new HttpClient(mockHandler);
99+
100+ mockHandler.Enqueue(x =>
101+ {
102+ Assert.Equal(HttpMethod.Post, x.Method);
103+ Assert.Equal(ImgurApi.UploadEndpoint, x.RequestUri);
104+
105+ return new HttpResponseMessage(HttpStatusCode.BadRequest)
106+ {
107+ Content = new StringContent(@"<?xml version=""1.0"" encoding=""utf-8""?>
108+<data type=""array"" success=""0"" status=""400"">
109+ <error>No image data was sent to the upload api</error>
110+ <request>/3/image.xml</request>
111+ <method>POST</method>
112+</data>"),
113+ };
114+ });
115+
116+ var imgurApi = new ImgurApi("fake_api_key", http);
117+ using var mediaItem = TestUtils.CreateDummyMediaItem();
118+ await Assert.ThrowsAsync<WebApiException>(
119+ () => imgurApi.UploadFileAsync(mediaItem, "てすと")
120+ );
121+
122+ Assert.Equal(0, mockHandler.QueueCount);
123+ }
124+
125+ [Fact]
126+ public async Task UploadFileAsync_InvalidResponseTest()
127+ {
128+ using var mockHandler = new HttpMessageHandlerMock();
129+ using var http = new HttpClient(mockHandler);
130+
131+ mockHandler.Enqueue(x =>
132+ {
133+ Assert.Equal(HttpMethod.Post, x.Method);
134+ Assert.Equal(ImgurApi.UploadEndpoint, x.RequestUri);
135+
136+ return new HttpResponseMessage(HttpStatusCode.BadRequest)
137+ {
138+ Content = new StringContent("INVALID RESPONSE"),
139+ };
140+ });
141+
142+ var imgurApi = new ImgurApi("fake_api_key", http);
143+ using var mediaItem = TestUtils.CreateDummyMediaItem();
144+ await Assert.ThrowsAsync<WebApiException>(
145+ () => imgurApi.UploadFileAsync(mediaItem, "てすと")
146+ );
147+
148+ Assert.Equal(0, mockHandler.QueueCount);
149+ }
150+ }
151+}
--- /dev/null
+++ b/OpenTween.Tests/Api/MobypictureApiTest.cs
@@ -0,0 +1,92 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2022 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;
26+using System.Net.Http;
27+using System.Text;
28+using System.Threading.Tasks;
29+using Xunit;
30+
31+namespace OpenTween.Api
32+{
33+ public class MobypictureApiTest
34+ {
35+ [Fact]
36+ public async Task UploadFileAsync_Test()
37+ {
38+ using var mockHandler = new HttpMessageHandlerMock();
39+ using var http = new HttpClient(mockHandler);
40+
41+ mockHandler.Enqueue(x =>
42+ {
43+ Assert.Equal(HttpMethod.Post, x.Method);
44+ Assert.Equal(MobypictureApi.UploadEndpoint, x.RequestUri);
45+
46+ return new HttpResponseMessage(HttpStatusCode.OK)
47+ {
48+ Content = new StringContent(@"<?xml version=""1.0"" encoding=""utf-8""?>
49+<rsp>
50+ <media>
51+ <mediaurl>https://www.mobypicture.com/user/OpenTween/view/00000000</mediaurl>
52+ </media>
53+</rsp>"),
54+ };
55+ });
56+
57+ var mobypictureApi = new MobypictureApi("fake_api_key", http);
58+ using var mediaItem = TestUtils.CreateDummyMediaItem();
59+ var uploadedUrl = await mobypictureApi.UploadFileAsync(mediaItem, "てすと")
60+ .ConfigureAwait(false);
61+ Assert.Equal("https://www.mobypicture.com/user/OpenTween/view/00000000", uploadedUrl);
62+
63+ Assert.Equal(0, mockHandler.QueueCount);
64+ }
65+
66+ [Fact]
67+ public async Task UploadFileAsync_InvalidResponseTest()
68+ {
69+ using var mockHandler = new HttpMessageHandlerMock();
70+ using var http = new HttpClient(mockHandler);
71+
72+ mockHandler.Enqueue(x =>
73+ {
74+ Assert.Equal(HttpMethod.Post, x.Method);
75+ Assert.Equal(MobypictureApi.UploadEndpoint, x.RequestUri);
76+
77+ return new HttpResponseMessage(HttpStatusCode.OK)
78+ {
79+ Content = new StringContent("INVALID RESPONSE"),
80+ };
81+ });
82+
83+ var mobypictureApi = new MobypictureApi("fake_api_key", http);
84+ using var mediaItem = TestUtils.CreateDummyMediaItem();
85+ await Assert.ThrowsAsync<WebApiException>(
86+ () => mobypictureApi.UploadFileAsync(mediaItem, "てすと")
87+ );
88+
89+ Assert.Equal(0, mockHandler.QueueCount);
90+ }
91+ }
92+}
--- /dev/null
+++ b/OpenTween.Tests/MediaUploadServices/ImgurTest.cs
@@ -0,0 +1,101 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2022 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.Text;
27+using System.Threading.Tasks;
28+using System.Xml.Linq;
29+using Moq;
30+using OpenTween.Api;
31+using OpenTween.Api.DataModel;
32+using Xunit;
33+
34+namespace OpenTween.MediaUploadServices
35+{
36+ public class ImgurTest
37+ {
38+ [Fact]
39+ public async Task UploadAsync_Test()
40+ {
41+ var mockApi = new Mock<IImgurApi>();
42+ var imgurApi = mockApi.Object;
43+
44+ var imgur = new Imgur(imgurApi, TwitterConfiguration.DefaultConfiguration());
45+ using var mediaItem = TestUtils.CreateDummyMediaItem();
46+
47+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
48+ .ReturnsAsync("https://i.imgur.com/aaaaaaa.png");
49+
50+ var param = new PostStatusParams
51+ {
52+ Text = "てすと",
53+ };
54+ await imgur.UploadAsync(new[] { mediaItem }, param);
55+
56+ Assert.Equal("てすと https://i.imgur.com/aaaaaaa.png", param.Text);
57+ }
58+
59+ [Fact]
60+ public async Task UploadAsync_ErrorResponseTest()
61+ {
62+ var mockApi = new Mock<IImgurApi>();
63+ var imgurApi = mockApi.Object;
64+
65+ var imgur = new Imgur(imgurApi, TwitterConfiguration.DefaultConfiguration());
66+ using var mediaItem = TestUtils.CreateDummyMediaItem();
67+
68+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
69+ .Throws<WebApiException>();
70+
71+ var param = new PostStatusParams
72+ {
73+ Text = "てすと",
74+ };
75+ await Assert.ThrowsAsync<WebApiException>(
76+ () => imgur.UploadAsync(new[] { mediaItem }, param)
77+ );
78+ }
79+
80+ [Fact]
81+ public async Task UploadAsync_TimeoutTest()
82+ {
83+ var mockApi = new Mock<IImgurApi>();
84+ var imgurApi = mockApi.Object;
85+
86+ var imgur = new Imgur(imgurApi, TwitterConfiguration.DefaultConfiguration());
87+ using var mediaItem = TestUtils.CreateDummyMediaItem();
88+
89+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
90+ .Throws<OperationCanceledException>();
91+
92+ var param = new PostStatusParams
93+ {
94+ Text = "てすと",
95+ };
96+ await Assert.ThrowsAsync<WebApiException>(
97+ () => imgur.UploadAsync(new[] { mediaItem }, param)
98+ );
99+ }
100+ }
101+}
--- /dev/null
+++ b/OpenTween.Tests/MediaUploadServices/MobypictureTest.cs
@@ -0,0 +1,99 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2022 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.Text;
26+using System.Threading.Tasks;
27+using Moq;
28+using OpenTween.Api;
29+using OpenTween.Api.DataModel;
30+using Xunit;
31+
32+namespace OpenTween.MediaUploadServices
33+{
34+ public class MobypictureTest
35+ {
36+ [Fact]
37+ public async Task UploadAsync_Test()
38+ {
39+ var mockApi = new Mock<IMobypictureApi>();
40+ var mobypictureApi = mockApi.Object;
41+
42+ var mobypicture = new Mobypicture(mobypictureApi, TwitterConfiguration.DefaultConfiguration());
43+ using var mediaItem = TestUtils.CreateDummyMediaItem();
44+
45+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
46+ .ReturnsAsync("https://www.mobypicture.com/user/OpenTween/view/00000000");
47+
48+ var param = new PostStatusParams
49+ {
50+ Text = "てすと",
51+ };
52+ await mobypicture.UploadAsync(new[] { mediaItem }, param);
53+
54+ Assert.Equal("てすと https://www.mobypicture.com/user/OpenTween/view/00000000", param.Text);
55+ }
56+
57+ [Fact]
58+ public async Task UploadAsync_ErrorResponseTest()
59+ {
60+ var mockApi = new Mock<IMobypictureApi>();
61+ var mobypictureApi = mockApi.Object;
62+
63+ var mobypicture = new Mobypicture(mobypictureApi, TwitterConfiguration.DefaultConfiguration());
64+ using var mediaItem = TestUtils.CreateDummyMediaItem();
65+
66+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
67+ .Throws<WebApiException>();
68+
69+ var param = new PostStatusParams
70+ {
71+ Text = "てすと",
72+ };
73+ await Assert.ThrowsAsync<WebApiException>(
74+ () => mobypicture.UploadAsync(new[] { mediaItem }, param)
75+ );
76+ }
77+
78+ [Fact]
79+ public async Task UploadAsync_TimeoutTest()
80+ {
81+ var mockApi = new Mock<IMobypictureApi>();
82+ var mobypictureApi = mockApi.Object;
83+
84+ var mobypicture = new Mobypicture(mobypictureApi, TwitterConfiguration.DefaultConfiguration());
85+ using var mediaItem = TestUtils.CreateDummyMediaItem();
86+
87+ mockApi.Setup((x) => x.UploadFileAsync(mediaItem, "てすと"))
88+ .Throws<OperationCanceledException>();
89+
90+ var param = new PostStatusParams
91+ {
92+ Text = "てすと",
93+ };
94+ await Assert.ThrowsAsync<WebApiException>(
95+ () => mobypicture.UploadAsync(new[] { mediaItem }, param)
96+ );
97+ }
98+ }
99+}
--- a/OpenTween.Tests/TestUtils.cs
+++ b/OpenTween.Tests/TestUtils.cs
@@ -107,6 +107,9 @@ namespace OpenTween
107107 }
108108 }
109109
110+ public static MemoryImageMediaItem CreateDummyMediaItem()
111+ => new MemoryImageMediaItem(CreateDummyImage());
112+
110113 public static void FireEvent<T>(T control, string eventName) where T : Control
111114 => TestUtils.FireEvent(control, eventName, EventArgs.Empty);
112115
--- /dev/null
+++ b/OpenTween/Api/ImgurApi.cs
@@ -0,0 +1,121 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2013 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+#nullable enable
23+
24+using System;
25+using System.Collections.Generic;
26+using System.Linq;
27+using System.Net.Http;
28+using System.Net.Http.Headers;
29+using System.Text;
30+using System.Threading.Tasks;
31+using System.Xml;
32+using System.Xml.Linq;
33+using System.Xml.XPath;
34+using OpenTween.Connection;
35+
36+namespace OpenTween.Api
37+{
38+ public class ImgurApi : IImgurApi
39+ {
40+ private readonly string clientId;
41+ private readonly HttpClient http;
42+
43+ public static readonly Uri UploadEndpoint = new Uri("https://api.imgur.com/3/image.xml");
44+
45+ public ImgurApi()
46+ : this(ApplicationSettings.ImgurClientID, null)
47+ {
48+ }
49+
50+ public ImgurApi(string clientId, HttpClient? http)
51+ {
52+ this.clientId = clientId;
53+
54+ if (http != null)
55+ {
56+ this.http = http;
57+ }
58+ else
59+ {
60+ this.http = Networking.CreateHttpClient(Networking.CreateHttpClientHandler());
61+ this.http.Timeout = Networking.UploadImageTimeout;
62+ }
63+ }
64+
65+ public async Task<string> UploadFileAsync(IMediaItem item, string title)
66+ {
67+ using var response = await this.SendRequestAsync(item, title)
68+ .ConfigureAwait(false);
69+
70+ var responseText = await response.Content.ReadAsStringAsync()
71+ .ConfigureAwait(false);
72+
73+ XDocument responseXml;
74+ try
75+ {
76+ responseXml = XDocument.Parse(responseText);
77+ }
78+ catch (XmlException ex)
79+ {
80+ var errorMessage = response.IsSuccessStatusCode ? "Invalid response" : response.StatusCode.ToString();
81+ throw new WebApiException("Err:" + errorMessage, responseText, ex);
82+ }
83+
84+ var imageElm = responseXml.Element("data");
85+ if (imageElm?.Attribute("success")?.Value != "1")
86+ {
87+ var errorMessage = imageElm?.Element("error")?.Value ?? "Invalid response";
88+ throw new WebApiException("Err:" + errorMessage, responseText);
89+ }
90+
91+ var imageUrl = responseXml.XPathSelectElement("/data/link")?.Value;
92+ if (imageUrl == null)
93+ throw new WebApiException("Err:Invalid response", responseText);
94+
95+ return imageUrl.Trim();
96+ }
97+
98+ private async Task<HttpResponseMessage> SendRequestAsync(IMediaItem item, string title)
99+ {
100+ using var content = new MultipartFormDataContent();
101+ using var mediaStream = item.OpenRead();
102+ using var mediaContent = new StreamContent(mediaStream);
103+ using var titleContent = new StringContent(title);
104+
105+ content.Add(mediaContent, "image", item.Name);
106+ content.Add(titleContent, "title");
107+
108+ using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint);
109+ request.Headers.Authorization = new AuthenticationHeaderValue("Client-ID", this.clientId);
110+ request.Content = content;
111+
112+ return await this.http.SendAsync(request)
113+ .ConfigureAwait(false);
114+ }
115+ }
116+
117+ public interface IImgurApi
118+ {
119+ Task<string> UploadFileAsync(IMediaItem item, string title);
120+ }
121+}
--- /dev/null
+++ b/OpenTween/Api/MobypictureApi.cs
@@ -0,0 +1,125 @@
1+// OpenTween - Client of Twitter
2+// Copyright (c) 2014 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+#nullable enable
23+
24+using System;
25+using System.Collections.Generic;
26+using System.Linq;
27+using System.Net.Http;
28+using System.Text;
29+using System.Threading.Tasks;
30+using System.Xml;
31+using System.Xml.Linq;
32+using System.Xml.XPath;
33+using OpenTween.Connection;
34+
35+namespace OpenTween.Api
36+{
37+ public class MobypictureApi : IMobypictureApi
38+ {
39+ private readonly string apiKey;
40+ private readonly HttpClient http;
41+
42+ public static readonly Uri UploadEndpoint = new Uri("https://api.mobypicture.com/2.0/upload.xml");
43+
44+ private static readonly Uri OAuthRealm = new Uri("http://api.twitter.com/");
45+ private static readonly Uri AuthServiceProvider = new Uri("https://api.twitter.com/1.1/account/verify_credentials.json");
46+
47+ public MobypictureApi(TwitterApi twitterApi)
48+ : this(ApplicationSettings.MobypictureKey, twitterApi)
49+ {
50+ }
51+
52+ public MobypictureApi(string apiKey, TwitterApi twitterApi)
53+ {
54+ this.apiKey = apiKey;
55+
56+ var handler = twitterApi.CreateOAuthEchoHandler(AuthServiceProvider, OAuthRealm);
57+ this.http = Networking.CreateHttpClient(handler);
58+ this.http.Timeout = Networking.UploadImageTimeout;
59+ }
60+
61+ public MobypictureApi(string apiKey, HttpClient http)
62+ {
63+ this.apiKey = apiKey;
64+ this.http = http;
65+ }
66+
67+ /// <summary>
68+ /// 画像のアップロードを行います
69+ /// </summary>
70+ /// <exception cref="ApiKeyDecryptException"/>
71+ /// <exception cref="WebApiException"/>
72+ /// <exception cref="XmlException"/>
73+ public async Task<string> UploadFileAsync(IMediaItem item, string message)
74+ {
75+ using var response = await this.SendRequestAsync(item, message)
76+ .ConfigureAwait(false);
77+
78+ var responseText = await response.Content.ReadAsStringAsync()
79+ .ConfigureAwait(false);
80+
81+ XDocument responseXml;
82+ try
83+ {
84+ responseXml = XDocument.Parse(responseText);
85+ }
86+ catch (XmlException ex)
87+ {
88+ var errorMessage = response.IsSuccessStatusCode ? "Invalid response" : response.StatusCode.ToString();
89+ throw new WebApiException("Err:" + errorMessage, responseText, ex);
90+ }
91+
92+ var imageUrlElm = responseXml.XPathSelectElement("/rsp/media/mediaurl");
93+ if (imageUrlElm == null)
94+ throw new WebApiException("Invalid API response", responseText);
95+
96+ return imageUrlElm.Value.Trim();
97+ }
98+
99+ private async Task<HttpResponseMessage> SendRequestAsync(IMediaItem item, string message)
100+ {
101+ // 参照: http://developers.mobypicture.com/documentation/2-0/upload/
102+
103+ using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint);
104+ using var multipart = new MultipartFormDataContent();
105+ request.Content = multipart;
106+
107+ using var apiKeyContent = new StringContent(this.apiKey);
108+ using var messageContent = new StringContent(message);
109+ using var mediaStream = item.OpenRead();
110+ using var mediaContent = new StreamContent(mediaStream);
111+
112+ multipart.Add(apiKeyContent, "key");
113+ multipart.Add(messageContent, "message");
114+ multipart.Add(mediaContent, "media", item.Name);
115+
116+ return await this.http.SendAsync(request)
117+ .ConfigureAwait(false);
118+ }
119+ }
120+
121+ public interface IMobypictureApi
122+ {
123+ Task<string> UploadFileAsync(IMediaItem item, string message);
124+ }
125+}
--- a/OpenTween/MediaSelector.cs
+++ b/OpenTween/MediaSelector.cs
@@ -24,16 +24,16 @@
2424 using System;
2525 using System.Collections.Generic;
2626 using System.ComponentModel;
27-using System.Drawing;
2827 using System.Data;
28+using System.Diagnostics.CodeAnalysis;
29+using System.Drawing;
2930 using System.IO;
3031 using System.Linq;
3132 using System.Text;
3233 using System.Threading.Tasks;
3334 using System.Windows.Forms;
3435 using OpenTween.Api.DataModel;
35-using OpenTween.Connection;
36-using System.Diagnostics.CodeAnalysis;
36+using OpenTween.MediaUploadServices;
3737
3838 namespace OpenTween
3939 {
--- a/OpenTween/Connection/IMediaUploadService.cs
+++ b/OpenTween/MediaUploadServices/IMediaUploadService.cs
@@ -29,7 +29,7 @@ using System.Text;
2929 using System.Threading.Tasks;
3030 using OpenTween.Api.DataModel;
3131
32-namespace OpenTween.Connection
32+namespace OpenTween.MediaUploadServices
3333 {
3434 /// <summary>
3535 /// Twitterでの画像の共有に使用できるサービスを表すインタフェース
--- a/OpenTween/Connection/Imgur.cs
+++ b/OpenTween/MediaUploadServices/Imgur.cs
@@ -23,16 +23,13 @@
2323
2424 using System;
2525 using System.Collections.Generic;
26-using System.IO;
2726 using System.Linq;
28-using System.Net.Http;
29-using System.Net.Http.Headers;
3027 using System.Text;
3128 using System.Threading.Tasks;
32-using System.Xml.Linq;
29+using OpenTween.Api;
3330 using OpenTween.Api.DataModel;
3431
35-namespace OpenTween.Connection
32+namespace OpenTween.MediaUploadServices
3633 {
3734 public class Imgur : IMediaUploadService
3835 {
@@ -51,15 +48,19 @@ namespace OpenTween.Connection
5148 ".xcf",
5249 };
5350
54- private readonly ImgurApi imgurApi;
51+ private readonly IImgurApi imgurApi;
5552
5653 private TwitterConfiguration twitterConfig;
5754
5855 public Imgur(TwitterConfiguration twitterConfig)
56+ : this(new ImgurApi(), twitterConfig)
5957 {
60- this.twitterConfig = twitterConfig;
58+ }
6159
62- this.imgurApi = new ImgurApi();
60+ public Imgur(IImgurApi imgurApi, TwitterConfiguration twitterConfig)
61+ {
62+ this.imgurApi = imgurApi;
63+ this.twitterConfig = twitterConfig;
6364 }
6465
6566 public int MaxMediaCount => 1;
@@ -107,31 +108,19 @@ namespace OpenTween.Connection
107108 if (!item.Exists)
108109 throw new ArgumentException("Err:Media not found.");
109110
110- XDocument xml;
111111 try
112112 {
113- xml = await this.imgurApi.UploadFileAsync(item, postParams.Text)
113+ var imageUrl = await this.imgurApi.UploadFileAsync(item, postParams.Text)
114114 .ConfigureAwait(false);
115- }
116- catch (HttpRequestException ex)
117- {
118- throw new WebApiException("Err:" + ex.Message, ex);
115+
116+ postParams.Text += " " + imageUrl;
117+
118+ return postParams;
119119 }
120120 catch (OperationCanceledException ex)
121121 {
122122 throw new WebApiException("Err:Timeout", ex);
123123 }
124-
125- var imageElm = xml.Element("data");
126-
127- if (imageElm.Attribute("success").Value != "1")
128- throw new WebApiException("Err:" + imageElm.Attribute("status").Value);
129-
130- var imageUrl = imageElm.Element("link").Value;
131-
132- postParams.Text += " " + imageUrl.Trim();
133-
134- return postParams;
135124 }
136125
137126 public int GetReservedTextLength(int mediaCount)
@@ -139,43 +128,5 @@ namespace OpenTween.Connection
139128
140129 public void UpdateTwitterConfiguration(TwitterConfiguration config)
141130 => this.twitterConfig = config;
142-
143- public class ImgurApi
144- {
145- private readonly HttpClient http;
146-
147- private static readonly Uri UploadEndpoint = new Uri("https://api.imgur.com/3/image.xml");
148-
149- public ImgurApi()
150- {
151- this.http = Networking.CreateHttpClient(Networking.CreateHttpClientHandler());
152- this.http.Timeout = Networking.UploadImageTimeout;
153- }
154-
155- public async Task<XDocument> UploadFileAsync(IMediaItem item, string title)
156- {
157- using var content = new MultipartFormDataContent();
158- using var mediaStream = item.OpenRead();
159- using var mediaContent = new StreamContent(mediaStream);
160- using var titleContent = new StringContent(title);
161-
162- content.Add(mediaContent, "image", item.Name);
163- content.Add(titleContent, "title");
164-
165- using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint);
166- request.Headers.Authorization = new AuthenticationHeaderValue("Client-ID", ApplicationSettings.ImgurClientID);
167- request.Content = content;
168-
169- using var response = await this.http.SendAsync(request)
170- .ConfigureAwait(false);
171-
172- response.EnsureSuccessStatusCode();
173-
174- using var stream = await response.Content.ReadAsStreamAsync()
175- .ConfigureAwait(false);
176-
177- return XDocument.Load(stream);
178- }
179- }
180131 }
181132 }
--- a/OpenTween/Connection/Mobypicture.cs
+++ b/OpenTween/MediaUploadServices/Mobypicture.cs
@@ -23,20 +23,13 @@
2323
2424 using System;
2525 using System.Collections.Generic;
26-using System.IO;
2726 using System.Linq;
28-using System.Net;
29-using System.Net.Http;
3027 using System.Text;
3128 using System.Threading.Tasks;
32-using System.Windows.Forms;
33-using System.Xml;
34-using System.Xml.Linq;
35-using System.Xml.XPath;
3629 using OpenTween.Api;
3730 using OpenTween.Api.DataModel;
3831
39-namespace OpenTween.Connection
32+namespace OpenTween.MediaUploadServices
4033 {
4134 public class Mobypicture : IMediaUploadService
4235 {
@@ -75,15 +68,19 @@ namespace OpenTween.Connection
7568 ".3gp",
7669 };
7770
78- private readonly MobypictureApi mobypictureApi;
71+ private readonly IMobypictureApi mobypictureApi;
7972
8073 private TwitterConfiguration twitterConfig;
8174
8275 public Mobypicture(Twitter twitter, TwitterConfiguration twitterConfig)
76+ : this(new MobypictureApi(twitter.Api), twitterConfig)
8377 {
84- this.twitterConfig = twitterConfig ?? throw new ArgumentNullException(nameof(twitterConfig));
78+ }
8579
86- this.mobypictureApi = new MobypictureApi(twitter.Api);
80+ public Mobypicture(IMobypictureApi mobypictureApi, TwitterConfiguration twitterConfig)
81+ {
82+ this.mobypictureApi = mobypictureApi;
83+ this.twitterConfig = twitterConfig ?? throw new ArgumentNullException(nameof(twitterConfig));
8784 }
8885
8986 public int MaxMediaCount => 1;
@@ -131,16 +128,19 @@ namespace OpenTween.Connection
131128 if (!item.Exists)
132129 throw new ArgumentException("Err:Media not found.");
133130
134- var xml = await this.mobypictureApi.UploadFileAsync(item, postParams.Text)
135- .ConfigureAwait(false);
136-
137- var imageUrlElm = xml.XPathSelectElement("/rsp/media/mediaurl");
138- if (imageUrlElm == null)
139- throw new WebApiException("Invalid API response", xml.ToString());
131+ try
132+ {
133+ var imageUrl = await this.mobypictureApi.UploadFileAsync(item, postParams.Text)
134+ .ConfigureAwait(false);
140135
141- postParams.Text += " " + imageUrlElm.Value.Trim();
136+ postParams.Text += " " + imageUrl;
142137
143- return postParams;
138+ return postParams;
139+ }
140+ catch (OperationCanceledException ex)
141+ {
142+ throw new WebApiException("Err:Timeout", ex);
143+ }
144144 }
145145
146146 public int GetReservedTextLength(int mediaCount)
@@ -148,57 +148,5 @@ namespace OpenTween.Connection
148148
149149 public void UpdateTwitterConfiguration(TwitterConfiguration config)
150150 => this.twitterConfig = config;
151-
152- public class MobypictureApi
153- {
154- private readonly HttpClient http;
155-
156- private static readonly Uri UploadEndpoint = new Uri("https://api.mobypicture.com/2.0/upload.xml");
157-
158- private static readonly Uri OAuthRealm = new Uri("http://api.twitter.com/");
159- private static readonly Uri AuthServiceProvider = new Uri("https://api.twitter.com/1.1/account/verify_credentials.json");
160-
161- public MobypictureApi(TwitterApi twitterApi)
162- {
163- var handler = twitterApi.CreateOAuthEchoHandler(AuthServiceProvider, OAuthRealm);
164-
165- this.http = Networking.CreateHttpClient(handler);
166- this.http.Timeout = Networking.UploadImageTimeout;
167- }
168-
169- /// <summary>
170- /// 画像のアップロードを行います
171- /// </summary>
172- /// <exception cref="WebApiException"/>
173- /// <exception cref="XmlException"/>
174- public async Task<XDocument> UploadFileAsync(IMediaItem item, string message)
175- {
176- // 参照: http://developers.mobypicture.com/documentation/2-0/upload/
177-
178- using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint);
179- using var multipart = new MultipartFormDataContent();
180- request.Content = multipart;
181-
182- using var apiKeyContent = new StringContent(ApplicationSettings.MobypictureKey);
183- using var messageContent = new StringContent(message);
184- using var mediaStream = item.OpenRead();
185- using var mediaContent = new StreamContent(mediaStream);
186-
187- multipart.Add(apiKeyContent, "key");
188- multipart.Add(messageContent, "message");
189- multipart.Add(mediaContent, "media", item.Name);
190-
191- using var response = await this.http.SendAsync(request)
192- .ConfigureAwait(false);
193-
194- var responseText = await response.Content.ReadAsStringAsync()
195- .ConfigureAwait(false);
196-
197- if (!response.IsSuccessStatusCode)
198- throw new WebApiException(response.StatusCode.ToString(), responseText);
199-
200- return XDocument.Parse(responseText);
201- }
202- }
203151 }
204152 }
--- a/OpenTween/Connection/TwitterPhoto.cs
+++ b/OpenTween/MediaUploadServices/TwitterPhoto.cs
@@ -37,7 +37,7 @@ using System.Threading.Tasks;
3737 using OpenTween.Api.DataModel;
3838 using OpenTween.Setting;
3939
40-namespace OpenTween.Connection
40+namespace OpenTween.MediaUploadServices
4141 {
4242 public class TwitterPhoto : IMediaUploadService
4343 {
--- a/OpenTween/OpenTween.csproj
+++ b/OpenTween/OpenTween.csproj
@@ -90,8 +90,10 @@
9090 <Compile Include="Api\DataModel\TwitterUploadMediaResult.cs" />
9191 <Compile Include="Api\DataModel\TwitterUser.cs" />
9292 <Compile Include="Api\DataModel\TwitterApiAccessLevel.cs" />
93+ <Compile Include="Api\ImgurApi.cs" />
9394 <Compile Include="Api\JsonUtils.cs" />
9495 <Compile Include="Api\MicrosoftTranslatorApi.cs" />
96+ <Compile Include="Api\MobypictureApi.cs" />
9597 <Compile Include="Api\TwitterApi.cs" />
9698 <Compile Include="Api\TwitterApiException.cs" />
9799 <Compile Include="Api\TwitterApiStatus.cs" />
@@ -121,10 +123,10 @@
121123 </Compile>
122124 <Compile Include="Bing.cs" />
123125 <Compile Include="Connection\IApiConnection.cs" />
124- <Compile Include="Connection\IMediaUploadService.cs" />
125- <Compile Include="Connection\Imgur.cs" />
126+ <Compile Include="MediaUploadServices\IMediaUploadService.cs" />
127+ <Compile Include="MediaUploadServices\Imgur.cs" />
126128 <Compile Include="Connection\LazyJson.cs" />
127- <Compile Include="Connection\Mobypicture.cs" />
129+ <Compile Include="MediaUploadServices\Mobypicture.cs" />
128130 <Compile Include="Connection\Networking.cs" />
129131 <Compile Include="Connection\OAuthEchoHandler.cs" />
130132 <Compile Include="Connection\OAuthHandler.cs" />
@@ -242,7 +244,7 @@
242244 <Compile Include="ListManage.Designer.cs">
243245 <DependentUpon>ListManage.cs</DependentUpon>
244246 </Compile>
245- <Compile Include="Connection\TwitterPhoto.cs" />
247+ <Compile Include="MediaUploadServices\TwitterPhoto.cs" />
246248 <Compile Include="DetailsListView.cs">
247249 <SubType>Component</SubType>
248250 </Compile>
--- a/OpenTween/Tween.cs
+++ b/OpenTween/Tween.cs
@@ -53,6 +53,7 @@ using System.Windows.Forms;
5353 using OpenTween.Api;
5454 using OpenTween.Api.DataModel;
5555 using OpenTween.Connection;
56+using OpenTween.MediaUploadServices;
5657 using OpenTween.Models;
5758 using OpenTween.OpenTweenCustomControl;
5859 using OpenTween.Setting;
Show on old repository browser