diff --git a/CHANGELOG.md b/CHANGELOG.md index b9c0682..575072a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - [Profile Information](https://wiki.vg/Mojang_API#Profile_Information) endpoint. +- [Change skin](https://wiki.vg/Mojang_API#Change_Skin) endpoint. +- [Upload skin](https://wiki.vg/Mojang_API#Upload_Skin) endpoint. ## [0.2.1] - 2020-06-10 ### Added diff --git a/src/Api.php b/src/Api.php index b863a34..e457510 100644 --- a/src/Api.php +++ b/src/Api.php @@ -210,6 +210,7 @@ class Api { * * @throws GuzzleException * + * @deprecated * @url https://wiki.vg/Mojang_API#Upload_Skin */ public function uploadSkin(string $accessToken, string $accountUuid, $skinContents, bool $isSlim): void { @@ -573,6 +574,55 @@ class Api { ); } + /** + * @param string $accessToken + * @param \Psr\Http\Message\StreamInterface|resource|string $skinContents + * @param bool $isSlim + * + * @throws GuzzleException + * + * @url https://wiki.vg/Mojang_API#Upload_Skin + */ + public function uploadSkinByFile(string $accessToken, $skinContents, bool $isSlim): void { + $this->getClient()->request('POST', 'https://api.minecraftservices.com/minecraft/profile/skins', [ + RequestOptions::MULTIPART => [ + [ + 'name' => 'file', + 'contents' => $skinContents, + 'filename' => 'char.png', + ], + [ + 'name' => 'variant', + 'contents' => $isSlim ? 'slim' : 'classic', + ], + ], + RequestOptions::HEADERS => [ + 'Authorization' => 'Bearer ' . $accessToken, + ], + ]); + } + + /** + * @param string $accessToken + * @param string $skinUrl + * @param bool $isSlim + * + * @throws GuzzleException + * + * @url https://wiki.vg/Mojang_API#Change_Skin + */ + public function uploadSkinByUrl(string $accessToken, string $skinUrl, bool $isSlim): void { + $this->getClient()->request('POST', 'https://api.minecraftservices.com/minecraft/profile/skins', [ + RequestOptions::JSON => [ + 'variant' => $isSlim ? 'slim' : 'classic', + 'url' => $skinUrl, + ], + RequestOptions::HEADERS => [ + 'Authorization' => 'Bearer ' . $accessToken, + ], + ]); + } + /** * @return ClientInterface */ diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 0422b07..ac1d0e9 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -778,6 +778,28 @@ class ApiTest extends TestCase { $this->assertSame('Minecon2013', $result->getCapes()[0]->getAlias()); } + public function testUploadSkinByFile(): void { + $this->mockHandler->append(new Response(200)); + $this->api->uploadSkinByFile('mock access token', 'skin contents', false); + + /** @var \Psr\Http\Message\RequestInterface $request */ + $request = $this->history[0]['request']; + $this->assertSame('Bearer mock access token', $request->getHeaderLine('Authorization')); + $this->assertStringContainsString('skin contents', (string)$request->getBody()); + $this->assertStringContainsString('classic', (string)$request->getBody()); + } + + public function testUploadSkinByUrl(): void { + $this->mockHandler->append(new Response(200)); + $this->api->uploadSkinByUrl('mock access token', 'https://example.com/skin.png', false); + + /** @var \Psr\Http\Message\RequestInterface $request */ + $request = $this->history[0]['request']; + $this->assertSame('Bearer mock access token', $request->getHeaderLine('Authorization')); + $this->assertSame('application/json', $request->getHeaderLine('Content-Type')); + $this->assertSame('{"variant":"classic","url":"https:\/\/example.com\/skin.png"}', (string)$request->getBody()); + } + private function createResponse(int $statusCode, array $response): ResponseInterface { return new Response($statusCode, ['content-type' => 'json'], json_encode($response)); }