From 2456ab12d7ab38d3f831f00edd866a1769ee4092 Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Wed, 12 Jun 2024 01:54:09 +0900 Subject: [PATCH] =?utf8?q?TwitterMediaId=E3=82=92=E3=83=AC=E3=82=B3?= =?utf8?q?=E3=83=BC=E3=83=89=E5=9E=8B=E3=81=A8=E3=81=97=E3=81=A6=E8=BF=BD?= =?utf8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- OpenTween.Tests/Api/TwitterApiTest.cs | 13 ++++----- .../Twitter/CreateTweetFormatterTest.cs | 4 +-- OpenTween/Api/TwitterApi.cs | 26 +++++++++--------- OpenTween/MediaUploadServices/TwitterPhoto.cs | 11 ++++---- OpenTween/Models/INativeUploadMediaId.cs | 30 +++++++++++++++++++++ OpenTween/PostStatusParams.cs | 8 ++---- .../SocialProtocol/Twitter/CreateTweetFormatter.cs | 2 +- .../SocialProtocol/Twitter/CreateTweetParams.cs | 4 +-- OpenTween/SocialProtocol/Twitter/TwitterMediaId.cs | 31 ++++++++++++++++++++++ OpenTween/Tween.cs | 4 +-- OpenTween/Twitter.cs | 10 +++---- 11 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 OpenTween/Models/INativeUploadMediaId.cs create mode 100644 OpenTween/SocialProtocol/Twitter/TwitterMediaId.cs diff --git a/OpenTween.Tests/Api/TwitterApiTest.cs b/OpenTween.Tests/Api/TwitterApiTest.cs index 2177a36d..21b9c931 100644 --- a/OpenTween.Tests/Api/TwitterApiTest.cs +++ b/OpenTween.Tests/Api/TwitterApiTest.cs @@ -33,6 +33,7 @@ using Moq; using OpenTween.Api.DataModel; using OpenTween.Connection; using OpenTween.Models; +using OpenTween.SocialProtocol.Twitter; using Xunit; namespace OpenTween.Api @@ -275,7 +276,7 @@ namespace OpenTween.Api await twitterApi.StatusesUpdate( "hogehoge", replyToId: new("100"), - mediaIds: new[] { 10L, 20L }, + mediaIds: new TwitterMediaId[] { new("10"), new("20") }, autoPopulateReplyMetadata: true, excludeReplyUserIds: new TwitterUserId[] { new("100"), new("200") }, attachmentUrl: "https://twitter.com/twitterapi/status/22634515958" @@ -771,7 +772,7 @@ namespace OpenTween.Api using var twitterApi = new TwitterApi(); twitterApi.ApiConnection = mock.Object; - await twitterApi.DirectMessagesEventsNew(recipientId: new("12345"), text: "hogehoge", mediaId: 67890L); + await twitterApi.DirectMessagesEventsNew(recipientId: new("12345"), text: "hogehoge", mediaId: new("67890")); mock.VerifyAll(); } @@ -1348,7 +1349,7 @@ namespace OpenTween.Api using var twitterApi = new TwitterApi(); twitterApi.ApiConnection = mock.Object; - await twitterApi.MediaUploadAppend(mediaId: 11111L, segmentIndex: 1, media: media); + await twitterApi.MediaUploadAppend(mediaId: new("11111"), segmentIndex: 1, media: media); mock.VerifyAll(); } @@ -1370,7 +1371,7 @@ namespace OpenTween.Api using var twitterApi = new TwitterApi(); twitterApi.ApiConnection = mock.Object; - await twitterApi.MediaUploadFinalize(mediaId: 11111L) + await twitterApi.MediaUploadFinalize(mediaId: new("11111")) .IgnoreResponse(); mock.VerifyAll(); @@ -1396,7 +1397,7 @@ namespace OpenTween.Api using var twitterApi = new TwitterApi(); twitterApi.ApiConnection = mock.Object; - await twitterApi.MediaUploadStatus(mediaId: 11111L); + await twitterApi.MediaUploadStatus(mediaId: new("11111")); mock.VerifyAll(); } @@ -1413,7 +1414,7 @@ namespace OpenTween.Api using var twitterApi = new TwitterApi(); twitterApi.ApiConnection = mock.Object; - await twitterApi.MediaMetadataCreate(mediaId: 12345L, altText: "hogehoge"); + await twitterApi.MediaMetadataCreate(mediaId: new("12345"), altText: "hogehoge"); mock.VerifyAll(); } diff --git a/OpenTween.Tests/SocialProtocol/Twitter/CreateTweetFormatterTest.cs b/OpenTween.Tests/SocialProtocol/Twitter/CreateTweetFormatterTest.cs index 30007337..4a635887 100644 --- a/OpenTween.Tests/SocialProtocol/Twitter/CreateTweetFormatterTest.cs +++ b/OpenTween.Tests/SocialProtocol/Twitter/CreateTweetFormatterTest.cs @@ -165,11 +165,11 @@ namespace OpenTween.SocialProtocol.Twitter // 引用ツイートと画像添付は併用できないため attachment_url は使用しない(現在は許容されているかも?) var postParams = new PostStatusParams(Text: "hoge https://twitter.com/twitterapi/status/22634515958") { - MediaIds = new[] { 1234L }, + MediaIds = new[] { new TwitterMediaId("1234") }, }; var expected = new CreateTweetParams(Text: "hoge https://twitter.com/twitterapi/status/22634515958") { - MediaIds = new[] { 1234L }, + MediaIds = new[] { new TwitterMediaId("1234") }, }; Assert.Equal(expected, formatter.CreateParams(postParams)); } diff --git a/OpenTween/Api/TwitterApi.cs b/OpenTween/Api/TwitterApi.cs index 3277b8d7..a53d070f 100644 --- a/OpenTween/Api/TwitterApi.cs +++ b/OpenTween/Api/TwitterApi.cs @@ -32,6 +32,7 @@ using System.Threading.Tasks; using OpenTween.Api.DataModel; using OpenTween.Connection; using OpenTween.Models; +using OpenTween.SocialProtocol.Twitter; namespace OpenTween.Api { @@ -191,7 +192,7 @@ namespace OpenTween.Api public async Task> StatusesUpdate( string status, TwitterStatusId? replyToId, - IReadOnlyList? mediaIds, + IReadOnlyList? mediaIds, bool? autoPopulateReplyMetadata = null, IReadOnlyList? excludeReplyUserIds = null, string? attachmentUrl = null) @@ -207,7 +208,7 @@ namespace OpenTween.Api if (replyToId != null) param["in_reply_to_status_id"] = replyToId.Id; if (mediaIds != null && mediaIds.Count > 0) - param.Add("media_ids", string.Join(",", mediaIds)); + param.Add("media_ids", string.Join(",", mediaIds.Select(x => x.Id))); if (autoPopulateReplyMetadata != null) param["auto_populate_reply_metadata"] = autoPopulateReplyMetadata.Value ? "true" : "false"; if (excludeReplyUserIds != null && excludeReplyUserIds.Count > 0) @@ -613,7 +614,7 @@ namespace OpenTween.Api .ConfigureAwait(false); } - public async Task> DirectMessagesEventsNew(TwitterUserId recipientId, string text, long? mediaId = null) + public async Task> DirectMessagesEventsNew(TwitterUserId recipientId, string text, TwitterMediaId? mediaId = null) { var attachment = ""; if (mediaId != null) @@ -622,7 +623,7 @@ namespace OpenTween.Api "attachment": { "type": "media", "media": { - "id": "{{JsonUtils.EscapeJsonString(mediaId.ToString())}}" + "id": "{{JsonUtils.EscapeJsonString(mediaId.Id)}}" } } """; @@ -1106,7 +1107,7 @@ namespace OpenTween.Api return response.ReadAsLazyJson(); } - public async Task MediaUploadAppend(long mediaId, int segmentIndex, IMediaItem media) + public async Task MediaUploadAppend(TwitterMediaId mediaId, int segmentIndex, IMediaItem media) { var request = new PostMultipartRequest { @@ -1114,7 +1115,7 @@ namespace OpenTween.Api Query = new Dictionary { ["command"] = "APPEND", - ["media_id"] = mediaId.ToString(), + ["media_id"] = mediaId.Id, ["segment_index"] = segmentIndex.ToString(), }, Media = new Dictionary @@ -1128,7 +1129,7 @@ namespace OpenTween.Api .ConfigureAwait(false); } - public async Task> MediaUploadFinalize(long mediaId) + public async Task> MediaUploadFinalize(TwitterMediaId mediaId) { var request = new PostRequest { @@ -1136,7 +1137,7 @@ namespace OpenTween.Api Query = new Dictionary { ["command"] = "FINALIZE", - ["media_id"] = mediaId.ToString(), + ["media_id"] = mediaId.Id, }, }; @@ -1146,7 +1147,7 @@ namespace OpenTween.Api return response.ReadAsLazyJson(); } - public async Task MediaUploadStatus(long mediaId) + public async Task MediaUploadStatus(TwitterMediaId mediaId) { var request = new GetRequest { @@ -1154,7 +1155,7 @@ namespace OpenTween.Api Query = new Dictionary { ["command"] = "STATUS", - ["media_id"] = mediaId.ToString(), + ["media_id"] = mediaId.Id, }, }; @@ -1165,13 +1166,14 @@ namespace OpenTween.Api .ConfigureAwait(false); } - public async Task MediaMetadataCreate(long mediaId, string altText) + public async Task MediaMetadataCreate(TwitterMediaId mediaId, string altText) { + var escapedMediaId = JsonUtils.EscapeJsonString(mediaId.Id); var escapedAltText = JsonUtils.EscapeJsonString(altText); var request = new PostJsonRequest { RequestUri = new("https://upload.twitter.com/1.1/media/metadata/create.json"), - JsonString = $$$"""{"media_id": "{{{mediaId}}}", "alt_text": {"text": "{{{escapedAltText}}}"}}""", + JsonString = $$$"""{"media_id": "{{{escapedMediaId}}}", "alt_text": {"text": "{{{escapedAltText}}}"}}""", }; await this.Connection.SendAsync(request) diff --git a/OpenTween/MediaUploadServices/TwitterPhoto.cs b/OpenTween/MediaUploadServices/TwitterPhoto.cs index 717f5b4d..37545196 100644 --- a/OpenTween/MediaUploadServices/TwitterPhoto.cs +++ b/OpenTween/MediaUploadServices/TwitterPhoto.cs @@ -36,6 +36,7 @@ using System.Linq; using System.Threading.Tasks; using OpenTween.Api.DataModel; using OpenTween.Setting; +using OpenTween.SocialProtocol.Twitter; namespace OpenTween.MediaUploadServices { @@ -89,7 +90,7 @@ namespace OpenTween.MediaUploadServices throw new ArgumentException("Err:Media not found."); } - long[] mediaIds; + TwitterMediaId[] mediaIds; if (Twitter.DMSendTextRegex.IsMatch(postParams.Text)) mediaIds = new[] { await this.UploadMediaForDM(mediaItems).ConfigureAwait(false) }; @@ -106,7 +107,7 @@ namespace OpenTween.MediaUploadServices public void UpdateTwitterConfiguration(TwitterConfiguration config) => this.twitterConfig = config; - private async Task UploadMediaForTweet(IMediaItem[] mediaItems) + private async Task UploadMediaForTweet(IMediaItem[] mediaItems) { var uploadTasks = from m in mediaItems select this.UploadMediaItem(m, mediaCategory: null); @@ -117,7 +118,7 @@ namespace OpenTween.MediaUploadServices return mediaIds; } - private async Task UploadMediaForDM(IMediaItem[] mediaItems) + private async Task UploadMediaForDM(IMediaItem[] mediaItems) { if (mediaItems.Length > 1) throw new InvalidOperationException("Err:Can't attach multiple media to DM."); @@ -135,9 +136,9 @@ namespace OpenTween.MediaUploadServices return mediaId; } - private async Task UploadMediaItem(IMediaItem mediaItem, string? mediaCategory) + private async Task UploadMediaItem(IMediaItem mediaItem, string? mediaCategory) { - async Task UploadInternal(IMediaItem media, string? category) + async Task UploadInternal(IMediaItem media, string? category) { var mediaId = await this.tw.UploadMedia(media, category) .ConfigureAwait(false); diff --git a/OpenTween/Models/INativeUploadMediaId.cs b/OpenTween/Models/INativeUploadMediaId.cs new file mode 100644 index 00000000..6a427390 --- /dev/null +++ b/OpenTween/Models/INativeUploadMediaId.cs @@ -0,0 +1,30 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2024 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +namespace OpenTween.Models +{ + public interface INativeUploadMediaId + { + public string Id { get; } + } +} diff --git a/OpenTween/PostStatusParams.cs b/OpenTween/PostStatusParams.cs index 87419ea1..1e60ad7b 100644 --- a/OpenTween/PostStatusParams.cs +++ b/OpenTween/PostStatusParams.cs @@ -23,10 +23,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using OpenTween.Connection; using OpenTween.Models; namespace OpenTween @@ -34,9 +30,9 @@ namespace OpenTween public record PostStatusParams( string Text, PostClass? InReplyTo = null, - IReadOnlyList? MediaIds = null + IReadOnlyList? MediaIds = null ) { - public IReadOnlyList MediaIds { get; init; } = MediaIds ?? Array.Empty(); + public IReadOnlyList MediaIds { get; init; } = MediaIds ?? Array.Empty(); } } diff --git a/OpenTween/SocialProtocol/Twitter/CreateTweetFormatter.cs b/OpenTween/SocialProtocol/Twitter/CreateTweetFormatter.cs index 8308b182..0b5a4578 100644 --- a/OpenTween/SocialProtocol/Twitter/CreateTweetFormatter.cs +++ b/OpenTween/SocialProtocol/Twitter/CreateTweetFormatter.cs @@ -52,7 +52,7 @@ namespace OpenTween.SocialProtocol.Twitter var createParams = new CreateTweetParams( formState.Text, formState.InReplyTo, - formState.MediaIds + formState.MediaIds.Cast().ToArray() ); // DM の場合はこれ以降の処理を行わない diff --git a/OpenTween/SocialProtocol/Twitter/CreateTweetParams.cs b/OpenTween/SocialProtocol/Twitter/CreateTweetParams.cs index 7a9cde7d..00b47f0f 100644 --- a/OpenTween/SocialProtocol/Twitter/CreateTweetParams.cs +++ b/OpenTween/SocialProtocol/Twitter/CreateTweetParams.cs @@ -31,13 +31,13 @@ namespace OpenTween.SocialProtocol.Twitter public record CreateTweetParams( string Text, PostClass? InReplyTo = null, - IReadOnlyList? MediaIds = null, + IReadOnlyList? MediaIds = null, bool AutoPopulateReplyMetadata = false, IReadOnlyList? ExcludeReplyUserIds = null, string? AttachmentUrl = null ) { - public IReadOnlyList MediaIds { get; init; } = MediaIds ?? Array.Empty(); + public IReadOnlyList MediaIds { get; init; } = MediaIds ?? Array.Empty(); public IReadOnlyList ExcludeReplyUserIds { get; init; } = ExcludeReplyUserIds ?? Array.Empty(); diff --git a/OpenTween/SocialProtocol/Twitter/TwitterMediaId.cs b/OpenTween/SocialProtocol/Twitter/TwitterMediaId.cs new file mode 100644 index 00000000..9bbc7b63 --- /dev/null +++ b/OpenTween/SocialProtocol/Twitter/TwitterMediaId.cs @@ -0,0 +1,31 @@ +// OpenTween - Client of Twitter +// Copyright (c) 2024 kim_upsilon (@kim_upsilon) +// All rights reserved. +// +// This file is part of OpenTween. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see , or write to +// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, +// Boston, MA 02110-1301, USA. + +#nullable enable + +using OpenTween.Models; + +namespace OpenTween.SocialProtocol.Twitter +{ + public record TwitterMediaId( + string Id + ) : INativeUploadMediaId; +} diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index e4a93e7b..42b425d9 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -3336,13 +3336,13 @@ namespace OpenTween { var statusText = this.StatusText.Text; var replyToPost = this.inReplyTo is (var inReplyToStatusId, _) ? this.statuses[inReplyToStatusId] : null; - var mediaIds = Array.Empty(); + var mediaIds = Array.Empty(); if (setFakeMediaIds) { // 文字数計算のために仮の mediaId を設定する var useNativeUpload = this.ImageSelector.Visible && this.ImageSelector.Model.SelectedMediaService is { IsNativeUploadService: true }; if (useNativeUpload) - mediaIds = new[] { -1L }; + mediaIds = new[] { new TwitterMediaId("") }; } var statusParams = new PostStatusParams(statusText, replyToPost, mediaIds); diff --git a/OpenTween/Twitter.cs b/OpenTween/Twitter.cs index adb834bc..ebc79a7d 100644 --- a/OpenTween/Twitter.cs +++ b/OpenTween/Twitter.cs @@ -212,7 +212,7 @@ namespace OpenTween if (Twitter.DMSendTextRegex.IsMatch(param.Text)) { - var mediaId = param.MediaIds != null && param.MediaIds.Any() ? param.MediaIds[0] : (long?)null; + var mediaId = param.MediaIds?.FirstOrDefault(); await this.SendDirectMessage(param.Text, mediaId) .ConfigureAwait(false); @@ -264,7 +264,7 @@ namespace OpenTween return post; } - public async Task UploadMedia(IMediaItem item, string? mediaCategory = null) + public async Task UploadMedia(IMediaItem item, string? mediaCategory = null) { this.CheckAccountState(); @@ -283,7 +283,7 @@ namespace OpenTween var initMedia = await initResponse.LoadJsonAsync() .ConfigureAwait(false); - var mediaId = initMedia.MediaId; + var mediaId = new TwitterMediaId(initMedia.MediaIdStr); await this.Api.MediaUploadAppend(mediaId, 0, item) .ConfigureAwait(false); @@ -318,10 +318,10 @@ namespace OpenTween } succeeded: - return media.MediaId; + return mediaId; } - public async Task SendDirectMessage(string postStr, long? mediaId = null) + public async Task SendDirectMessage(string postStr, TwitterMediaId? mediaId = null) { this.CheckAccountState(); -- 2.11.0