1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
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)
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
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.
25 using System.Collections.Generic;
29 using System.Net.Http;
31 using System.Threading.Tasks;
32 using System.Windows.Forms;
34 using System.Xml.Linq;
35 using System.Xml.XPath;
37 using OpenTween.Api.DataModel;
39 namespace OpenTween.Connection
41 public class Mobypicture : IMediaUploadService
43 private static readonly long MaxFileSize = 5L * 1024 * 1024; // 上限不明
45 // 参照: http://developers.mobypicture.com/documentation/1-0/postmedia/
46 private static readonly IEnumerable<string> AllowedExtensions = new[]
78 private readonly MobypictureApi mobypictureApi;
80 private TwitterConfiguration twitterConfig;
82 public Mobypicture(Twitter twitter, TwitterConfiguration twitterConfig)
84 this.twitterConfig = twitterConfig ?? throw new ArgumentNullException(nameof(twitterConfig));
86 this.mobypictureApi = new MobypictureApi(twitter.Api);
89 public int MaxMediaCount => 1;
91 public string SupportedFormatsStrForDialog
95 var filterFormatExtensions = "";
96 foreach (var pictureExtension in AllowedExtensions)
98 filterFormatExtensions += '*' + pictureExtension + ';';
100 return "Media Files(" + filterFormatExtensions + ")|" + filterFormatExtensions;
104 public bool CanUseAltText => false;
106 public bool CheckFileExtension(string fileExtension)
107 => AllowedExtensions.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
109 public bool CheckFileSize(string fileExtension, long fileSize)
111 var maxFileSize = this.GetMaxFileSize(fileExtension);
112 return maxFileSize == null || fileSize <= maxFileSize.Value;
115 public long? GetMaxFileSize(string fileExtension)
118 public async Task<PostStatusParams> UploadAsync(IMediaItem[] mediaItems, PostStatusParams postParams)
120 if (mediaItems == null)
121 throw new ArgumentNullException(nameof(mediaItems));
123 if (mediaItems.Length != 1)
124 throw new ArgumentOutOfRangeException(nameof(mediaItems));
126 var item = mediaItems[0];
129 throw new ArgumentException("Err:Media not specified.");
132 throw new ArgumentException("Err:Media not found.");
134 var xml = await this.mobypictureApi.UploadFileAsync(item, postParams.Text)
135 .ConfigureAwait(false);
137 var imageUrlElm = xml.XPathSelectElement("/rsp/media/mediaurl");
138 if (imageUrlElm == null)
139 throw new WebApiException("Invalid API response", xml.ToString());
141 postParams.Text += " " + imageUrlElm.Value.Trim();
146 public int GetReservedTextLength(int mediaCount)
147 => this.twitterConfig.ShortUrlLength + 1;
149 public void UpdateTwitterConfiguration(TwitterConfiguration config)
150 => this.twitterConfig = config;
152 public class MobypictureApi
154 private readonly HttpClient http;
156 private static readonly Uri UploadEndpoint = new Uri("https://api.mobypicture.com/2.0/upload.xml");
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");
161 public MobypictureApi(TwitterApi twitterApi)
163 var handler = twitterApi.CreateOAuthEchoHandler(AuthServiceProvider, OAuthRealm);
165 this.http = Networking.CreateHttpClient(handler);
166 this.http.Timeout = Networking.UploadImageTimeout;
172 /// <exception cref="WebApiException"/>
173 /// <exception cref="XmlException"/>
174 public async Task<XDocument> UploadFileAsync(IMediaItem item, string message)
176 // 参照: http://developers.mobypicture.com/documentation/2-0/upload/
178 using var request = new HttpRequestMessage(HttpMethod.Post, UploadEndpoint);
179 using var multipart = new MultipartFormDataContent();
180 request.Content = multipart;
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);
187 multipart.Add(apiKeyContent, "key");
188 multipart.Add(messageContent, "message");
189 multipart.Add(mediaContent, "media", item.Name);
191 using var response = await this.http.SendAsync(request)
192 .ConfigureAwait(false);
194 var responseText = await response.Content.ReadAsStringAsync()
195 .ConfigureAwait(false);
197 if (!response.IsSuccessStatusCode)
198 throw new WebApiException(response.StatusCode.ToString(), responseText);
200 return XDocument.Parse(responseText);