Skip to content

Commit 6fb5163

Browse files
committed
bug symfony#58658 [Twitter][Notifier] Fix post INIT upload (matyo91)
This PR was merged into the 6.4 branch. Discussion ---------- [Twitter][Notifier] Fix post INIT upload | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Issues | Fix Twitter Notifier when attaching a media. <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead --> | License | MIT The procedure is described here : [https://developer.x.com/en/docs/x-api/v1/media/upload-media/api-reference/post-media-upload-init](https://developer.x.com/en/docs/x-api/v1/media/upload-media/api-reference/post-media-upload-init). This is tricky as on example Example Request the `media_type` query parameter value is raw encoded : `image/jpeg` or `video/mp4` for exemple. On Postman, query param value is raw encoded too on the interface, but with PHP CURL generated, it's [Percent encoded](https://developer.x.com/en/docs/authentication/oauth-1-0a/percent-encoding-parameters) or `rawurl encoded` : `video%252Fmp4`. I did test the CURL generation and it was OK. But it's not the case on Twitter Notifier component that produce `[Symfony\Component\Notifier\Exception\TransportException (32)] Could not authenticate you` (so this fix). <img width="1396" alt="postman" src="https://github.com/user-attachments/assets/172418bf-655f-4e55-87ec-f7a18e322103"> It did several tests, first I think it was the OAuth, but looking at the code on `Symfony\Component\Notifier\Bridge\Twitter\TwitterTransport::request` method and twitter docs, it was fine by comparing the php curl generated by postman and the curl generated by Symfony\Component\HttpClient\CurlHttpClient. - [https://developer.x.com/en/docs/authentication/oauth-1-0a/authorizing-a-request](https://developer.x.com/en/docs/authentication/oauth-1-0a/authorizing-a-request) - [https://developer.x.com/en/docs/authentication/oauth-1-0a/creating-a-signature](https://developer.x.com/en/docs/authentication/oauth-1-0a/creating-a-signature) The difference was on the query (look at media_type param that is raw). This one without the fix produce the Exception. ```bash [1:27:06][math@mathieus-mbp ~/Sites/darkwood/flow-live] (wave-function-collapse)$ bin/console app:wave-function-collapse -vvv 23:27:44 INFO [http_client] Request: "POST https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=56740&media_type=video/mp4&media_category=tweet_video" 23:27:44 INFO [http_client] Response: "401 https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=56740&media_type=video/mp4&media_category=tweet_video" In TwitterTransport.php line 247: [Symfony\Component\Notifier\Exception\TransportException (32)] Could not authenticate you Exception trace: at /Users/math/Sites/darkwood/flow-live/vendor/symfony/twitter-notifier/TwitterTransport.php:247 Symfony\Component\Notifier\Bridge\Twitter\TwitterTransport->processChunk() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/twitter-notifier/TwitterTransport.php:198 Symfony\Component\Notifier\Bridge\Twitter\TwitterTransport->uploadMedia() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/twitter-notifier/TwitterTransport.php:120 Symfony\Component\Notifier\Bridge\Twitter\TwitterTransport->doSend() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/notifier/Transport/AbstractTransport.php:80 Symfony\Component\Notifier\Transport\AbstractTransport->send() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/notifier/Transport/Transports.php:74 Symfony\Component\Notifier\Transport\Transports->send() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/notifier/Chatter.php:46 Symfony\Component\Notifier\Chatter->send() at /Users/math/Sites/darkwood/flow-live/src/Command/WaveFunctionCollapseCommand.php:61 App\Command\WaveFunctionCollapseCommand->execute() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/console/Command/Command.php:279 Symfony\Component\Console\Command\Command->run() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/console/Application.php:1047 Symfony\Component\Console\Application->doRunCommand() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/framework-bundle/Console/Application.php:123 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/console/Application.php:316 Symfony\Component\Console\Application->doRun() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/framework-bundle/Console/Application.php:77 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/console/Application.php:167 Symfony\Component\Console\Application->run() at /Users/math/Sites/darkwood/flow-live/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:49 Symfony\Component\Runtime\Runner\Symfony\ConsoleApplicationRunner->run() at /Users/math/Sites/darkwood/flow-live/vendor/autoload_runtime.php:29 require_once() at /Users/math/Sites/darkwood/flow-live/bin/console:15 app:wave-function-collapse [--width [WIDTH]] [--height [HEIGHT]] [--dataset [DATASET]] ``` And this one with the fix produce is OK. (look at media_type param that is rawurl encoded) ```bash [0:11:12][math@mathieus-mbp ~/Sites/darkwood/flow-live] (wave-function-collapse)$ bin/console app:wave-function-collapse -vvv 22:11:18 INFO [http_client] Request: "POST https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=56740&media_type=video%252Fmp4&media_category=tweet_video" 22:11:18 INFO [http_client] Response: "202 https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=56740&media_type=video%252Fmp4&media_category=tweet_video" 22:11:18 INFO [http_client] Request: "POST https://upload.twitter.com/1.1/media/upload.json?command=APPEND&media_id=1849574415295537152&segment_index=0" 22:11:18 INFO [http_client] Response: "204 https://upload.twitter.com/1.1/media/upload.json?command=APPEND&media_id=1849574415295537152&segment_index=0" 22:11:18 INFO [http_client] Request: "POST https://upload.twitter.com/1.1/media/upload.json?command=FINALIZE&media_id=1849574415295537152" 22:11:19 INFO [http_client] Response: "200 https://upload.twitter.com/1.1/media/upload.json?command=FINALIZE&media_id=1849574415295537152" 22:11:19 INFO [http_client] Request: "GET https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=1849574415295537152" 22:11:20 INFO [http_client] Response: "200 https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=1849574415295537152" 22:11:20 INFO [http_client] Request: "POST https://api.twitter.com/2/tweets" 22:11:20 INFO [http_client] Response: "201 https://api.twitter.com/2/tweets" ``` To reproduce it : ```php /** `@var` ChatterInterface $chatter */ $videoFile = new \Symfony\Component\Mime\Part\File('var/cache/dev/wave_function_collapse/wave_function_collapse_6716bd83ad525.mp4'); $message = (new ChatMessage('Daily Flow generation.', (new TwitterOptions())->attachVideo($videoFile)))->transport('twitter'); $chatter->send($message); ``` My tests were only with mp4 file. I didn't test with gif or media_category = 'subtitles' but I assume it's the same logic. It was a real test done with a tweeter account, so can't really reproduce it without creating your own keys [Twitter notifier docs](https://github.com/symfony/twitter-notifier) with read and write Twitter Access Token + Secret with a twitter account. Commits ------- 0d87e8e [Twitter][Notifier] Fix post INIT upload
2 parents 91acfa8 + 0d87e8e commit 6fb5163

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

src/Symfony/Component/Notifier/Bridge/Twitter/Tests/TwitterTransportTest.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ public function testTweetImage()
6666
$transport = $this->createTransport(new MockHttpClient((function () {
6767
yield function (string $method, string $url, array $options) {
6868
$this->assertSame('POST', $method);
69-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=tweet_image', $url);
69+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
70+
$this->assertArrayHasKey('body', $options);
71+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=tweet_image');
7072
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
7173

7274
return new MockResponse('{"media_id_string":"gif123"}');
@@ -127,15 +129,19 @@ public function testTweetVideo()
127129
$transport = $this->createTransport(new MockHttpClient((function () {
128130
yield function (string $method, string $url, array $options) {
129131
$this->assertSame('POST', $method);
130-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=tweet_video', $url);
132+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
133+
$this->assertArrayHasKey('body', $options);
134+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=tweet_video');
131135
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
132136

133137
return new MockResponse('{"media_id_string":"gif123"}');
134138
};
135139

136140
yield function (string $method, string $url, array $options) {
137141
$this->assertSame('POST', $method);
138-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=subtitles', $url);
142+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
143+
$this->assertArrayHasKey('body', $options);
144+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=subtitles');
139145
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
140146

141147
return new MockResponse('{"media_id_string":"sub234"}');

src/Symfony/Component/Notifier/Bridge/Twitter/TwitterTransport.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,32 +160,32 @@ private function uploadMedia(array $media): array
160160
'category' => $category,
161161
'owners' => $extraOwners,
162162
]) {
163-
$query = [
163+
$body = [
164164
'command' => 'INIT',
165165
'total_bytes' => $file->getSize(),
166166
'media_type' => $file->getContentType(),
167167
];
168168

169169
if ($category) {
170-
$query['media_category'] = $category;
170+
$body['media_category'] = $category;
171171
}
172172

173173
if ($extraOwners) {
174-
$query['additional_owners'] = implode(',', $extraOwners);
174+
$body['additional_owners'] = implode(',', $extraOwners);
175175
}
176176

177177
$pool[++$i] = $this->request('POST', '/1.1/media/upload.json', [
178-
'query' => $query,
178+
'body' => $body,
179179
'user_data' => [$i, null, 0, fopen($file->getPath(), 'r'), $alt, $subtitles],
180180
]);
181181

182182
if ($subtitles) {
183-
$query['total_bytes'] = $subtitles->getSize();
184-
$query['media_type'] = $subtitles->getContentType();
185-
$query['media_category'] = 'subtitles';
183+
$body['total_bytes'] = $subtitles->getSize();
184+
$body['media_type'] = $subtitles->getContentType();
185+
$body['media_category'] = 'subtitles';
186186

187187
$pool[++$i] = $this->request('POST', '/1.1/media/upload.json', [
188-
'query' => $query,
188+
'body' => $body,
189189
'user_data' => [$i, null, 0, fopen($subtitles->getPath(), 'r'), null, $subtitles],
190190
]);
191191
}

0 commit comments

Comments
 (0)