開発に使用するリポジトリ
Revision | a2eed78b35d5b95cfd641cbf06b04c4f33df0137 (tree) |
---|---|
Time | 2022-01-22 16:42:39 |
Author | upsilon <kim.upsilon@bucy...> |
Commiter | GitHub |
Merge pull request #87 from opentween/move-upload-api-classes
IMediaUploadService関連のクラスの名前空間を移動
@@ -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 | +} |
@@ -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 | +} |
@@ -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 | +} |
@@ -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 | +} |
@@ -107,6 +107,9 @@ namespace OpenTween | ||
107 | 107 | } |
108 | 108 | } |
109 | 109 | |
110 | + public static MemoryImageMediaItem CreateDummyMediaItem() | |
111 | + => new MemoryImageMediaItem(CreateDummyImage()); | |
112 | + | |
110 | 113 | public static void FireEvent<T>(T control, string eventName) where T : Control |
111 | 114 | => TestUtils.FireEvent(control, eventName, EventArgs.Empty); |
112 | 115 |
@@ -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 | +} |
@@ -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 | +} |
@@ -24,16 +24,16 @@ | ||
24 | 24 | using System; |
25 | 25 | using System.Collections.Generic; |
26 | 26 | using System.ComponentModel; |
27 | -using System.Drawing; | |
28 | 27 | using System.Data; |
28 | +using System.Diagnostics.CodeAnalysis; | |
29 | +using System.Drawing; | |
29 | 30 | using System.IO; |
30 | 31 | using System.Linq; |
31 | 32 | using System.Text; |
32 | 33 | using System.Threading.Tasks; |
33 | 34 | using System.Windows.Forms; |
34 | 35 | using OpenTween.Api.DataModel; |
35 | -using OpenTween.Connection; | |
36 | -using System.Diagnostics.CodeAnalysis; | |
36 | +using OpenTween.MediaUploadServices; | |
37 | 37 | |
38 | 38 | namespace OpenTween |
39 | 39 | { |
@@ -29,7 +29,7 @@ using System.Text; | ||
29 | 29 | using System.Threading.Tasks; |
30 | 30 | using OpenTween.Api.DataModel; |
31 | 31 | |
32 | -namespace OpenTween.Connection | |
32 | +namespace OpenTween.MediaUploadServices | |
33 | 33 | { |
34 | 34 | /// <summary> |
35 | 35 | /// Twitterでの画像の共有に使用できるサービスを表すインタフェース |
@@ -23,16 +23,13 @@ | ||
23 | 23 | |
24 | 24 | using System; |
25 | 25 | using System.Collections.Generic; |
26 | -using System.IO; | |
27 | 26 | using System.Linq; |
28 | -using System.Net.Http; | |
29 | -using System.Net.Http.Headers; | |
30 | 27 | using System.Text; |
31 | 28 | using System.Threading.Tasks; |
32 | -using System.Xml.Linq; | |
29 | +using OpenTween.Api; | |
33 | 30 | using OpenTween.Api.DataModel; |
34 | 31 | |
35 | -namespace OpenTween.Connection | |
32 | +namespace OpenTween.MediaUploadServices | |
36 | 33 | { |
37 | 34 | public class Imgur : IMediaUploadService |
38 | 35 | { |
@@ -51,15 +48,19 @@ namespace OpenTween.Connection | ||
51 | 48 | ".xcf", |
52 | 49 | }; |
53 | 50 | |
54 | - private readonly ImgurApi imgurApi; | |
51 | + private readonly IImgurApi imgurApi; | |
55 | 52 | |
56 | 53 | private TwitterConfiguration twitterConfig; |
57 | 54 | |
58 | 55 | public Imgur(TwitterConfiguration twitterConfig) |
56 | + : this(new ImgurApi(), twitterConfig) | |
59 | 57 | { |
60 | - this.twitterConfig = twitterConfig; | |
58 | + } | |
61 | 59 | |
62 | - this.imgurApi = new ImgurApi(); | |
60 | + public Imgur(IImgurApi imgurApi, TwitterConfiguration twitterConfig) | |
61 | + { | |
62 | + this.imgurApi = imgurApi; | |
63 | + this.twitterConfig = twitterConfig; | |
63 | 64 | } |
64 | 65 | |
65 | 66 | public int MaxMediaCount => 1; |
@@ -107,31 +108,19 @@ namespace OpenTween.Connection | ||
107 | 108 | if (!item.Exists) |
108 | 109 | throw new ArgumentException("Err:Media not found."); |
109 | 110 | |
110 | - XDocument xml; | |
111 | 111 | try |
112 | 112 | { |
113 | - xml = await this.imgurApi.UploadFileAsync(item, postParams.Text) | |
113 | + var imageUrl = await this.imgurApi.UploadFileAsync(item, postParams.Text) | |
114 | 114 | .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; | |
119 | 119 | } |
120 | 120 | catch (OperationCanceledException ex) |
121 | 121 | { |
122 | 122 | throw new WebApiException("Err:Timeout", ex); |
123 | 123 | } |
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; | |
135 | 124 | } |
136 | 125 | |
137 | 126 | public int GetReservedTextLength(int mediaCount) |
@@ -139,43 +128,5 @@ namespace OpenTween.Connection | ||
139 | 128 | |
140 | 129 | public void UpdateTwitterConfiguration(TwitterConfiguration config) |
141 | 130 | => 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 | - } | |
180 | 131 | } |
181 | 132 | } |
@@ -23,20 +23,13 @@ | ||
23 | 23 | |
24 | 24 | using System; |
25 | 25 | using System.Collections.Generic; |
26 | -using System.IO; | |
27 | 26 | using System.Linq; |
28 | -using System.Net; | |
29 | -using System.Net.Http; | |
30 | 27 | using System.Text; |
31 | 28 | using System.Threading.Tasks; |
32 | -using System.Windows.Forms; | |
33 | -using System.Xml; | |
34 | -using System.Xml.Linq; | |
35 | -using System.Xml.XPath; | |
36 | 29 | using OpenTween.Api; |
37 | 30 | using OpenTween.Api.DataModel; |
38 | 31 | |
39 | -namespace OpenTween.Connection | |
32 | +namespace OpenTween.MediaUploadServices | |
40 | 33 | { |
41 | 34 | public class Mobypicture : IMediaUploadService |
42 | 35 | { |
@@ -75,15 +68,19 @@ namespace OpenTween.Connection | ||
75 | 68 | ".3gp", |
76 | 69 | }; |
77 | 70 | |
78 | - private readonly MobypictureApi mobypictureApi; | |
71 | + private readonly IMobypictureApi mobypictureApi; | |
79 | 72 | |
80 | 73 | private TwitterConfiguration twitterConfig; |
81 | 74 | |
82 | 75 | public Mobypicture(Twitter twitter, TwitterConfiguration twitterConfig) |
76 | + : this(new MobypictureApi(twitter.Api), twitterConfig) | |
83 | 77 | { |
84 | - this.twitterConfig = twitterConfig ?? throw new ArgumentNullException(nameof(twitterConfig)); | |
78 | + } | |
85 | 79 | |
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)); | |
87 | 84 | } |
88 | 85 | |
89 | 86 | public int MaxMediaCount => 1; |
@@ -131,16 +128,19 @@ namespace OpenTween.Connection | ||
131 | 128 | if (!item.Exists) |
132 | 129 | throw new ArgumentException("Err:Media not found."); |
133 | 130 | |
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); | |
140 | 135 | |
141 | - postParams.Text += " " + imageUrlElm.Value.Trim(); | |
136 | + postParams.Text += " " + imageUrl; | |
142 | 137 | |
143 | - return postParams; | |
138 | + return postParams; | |
139 | + } | |
140 | + catch (OperationCanceledException ex) | |
141 | + { | |
142 | + throw new WebApiException("Err:Timeout", ex); | |
143 | + } | |
144 | 144 | } |
145 | 145 | |
146 | 146 | public int GetReservedTextLength(int mediaCount) |
@@ -148,57 +148,5 @@ namespace OpenTween.Connection | ||
148 | 148 | |
149 | 149 | public void UpdateTwitterConfiguration(TwitterConfiguration config) |
150 | 150 | => 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 | - } | |
203 | 151 | } |
204 | 152 | } |
@@ -37,7 +37,7 @@ using System.Threading.Tasks; | ||
37 | 37 | using OpenTween.Api.DataModel; |
38 | 38 | using OpenTween.Setting; |
39 | 39 | |
40 | -namespace OpenTween.Connection | |
40 | +namespace OpenTween.MediaUploadServices | |
41 | 41 | { |
42 | 42 | public class TwitterPhoto : IMediaUploadService |
43 | 43 | { |
@@ -90,8 +90,10 @@ | ||
90 | 90 | <Compile Include="Api\DataModel\TwitterUploadMediaResult.cs" /> |
91 | 91 | <Compile Include="Api\DataModel\TwitterUser.cs" /> |
92 | 92 | <Compile Include="Api\DataModel\TwitterApiAccessLevel.cs" /> |
93 | + <Compile Include="Api\ImgurApi.cs" /> | |
93 | 94 | <Compile Include="Api\JsonUtils.cs" /> |
94 | 95 | <Compile Include="Api\MicrosoftTranslatorApi.cs" /> |
96 | + <Compile Include="Api\MobypictureApi.cs" /> | |
95 | 97 | <Compile Include="Api\TwitterApi.cs" /> |
96 | 98 | <Compile Include="Api\TwitterApiException.cs" /> |
97 | 99 | <Compile Include="Api\TwitterApiStatus.cs" /> |
@@ -121,10 +123,10 @@ | ||
121 | 123 | </Compile> |
122 | 124 | <Compile Include="Bing.cs" /> |
123 | 125 | <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" /> | |
126 | 128 | <Compile Include="Connection\LazyJson.cs" /> |
127 | - <Compile Include="Connection\Mobypicture.cs" /> | |
129 | + <Compile Include="MediaUploadServices\Mobypicture.cs" /> | |
128 | 130 | <Compile Include="Connection\Networking.cs" /> |
129 | 131 | <Compile Include="Connection\OAuthEchoHandler.cs" /> |
130 | 132 | <Compile Include="Connection\OAuthHandler.cs" /> |
@@ -242,7 +244,7 @@ | ||
242 | 244 | <Compile Include="ListManage.Designer.cs"> |
243 | 245 | <DependentUpon>ListManage.cs</DependentUpon> |
244 | 246 | </Compile> |
245 | - <Compile Include="Connection\TwitterPhoto.cs" /> | |
247 | + <Compile Include="MediaUploadServices\TwitterPhoto.cs" /> | |
246 | 248 | <Compile Include="DetailsListView.cs"> |
247 | 249 | <SubType>Component</SubType> |
248 | 250 | </Compile> |
@@ -53,6 +53,7 @@ using System.Windows.Forms; | ||
53 | 53 | using OpenTween.Api; |
54 | 54 | using OpenTween.Api.DataModel; |
55 | 55 | using OpenTween.Connection; |
56 | +using OpenTween.MediaUploadServices; | |
56 | 57 | using OpenTween.Models; |
57 | 58 | using OpenTween.OpenTweenCustomControl; |
58 | 59 | using OpenTween.Setting; |