From caca406c7418c93416381c41fef4474656ac27c9 Mon Sep 17 00:00:00 2001 From: Woeler Date: Sun, 5 May 2024 20:11:06 +0200 Subject: [PATCH] [TASK] Initial Commit --- .github/workflows/cgl.yml | 17 ++++ .gitignore | 7 ++ .php-cs-fixer.php | 140 ++++++++++++++++++++++++++++++ composer.json | 32 +++++++ src/Api/Komoot.php | 164 +++++++++++++++++++++++++++++++++++ src/Enums/PrivacySetting.php | 12 +++ src/Enums/Sport.php | 34 ++++++++ 7 files changed, 406 insertions(+) create mode 100644 .github/workflows/cgl.yml create mode 100644 .gitignore create mode 100644 .php-cs-fixer.php create mode 100644 composer.json create mode 100644 src/Api/Komoot.php create mode 100644 src/Enums/PrivacySetting.php create mode 100644 src/Enums/Sport.php diff --git a/.github/workflows/cgl.yml b/.github/workflows/cgl.yml new file mode 100644 index 0000000..07c761d --- /dev/null +++ b/.github/workflows/cgl.yml @@ -0,0 +1,17 @@ +name: CGL +on: push +jobs: + cgl: + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + + - name: Composer install + run: composer install + + - name: CGL + run: composer cgl-ci \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e17f85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/.ddev/ +/.idea/ + +/vendor/ +/composer.lock +/test.php +/.php-cs-fixer.cache diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..795ba7a --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,140 @@ + ['syntax' => 'short'], + 'binary_operator_spaces' => [ + 'default' => 'single_space', + 'operators' => ['=>' => null] + ], + 'blank_line_after_namespace' => true, + 'blank_line_after_opening_tag' => true, + 'blank_line_before_statement' => [ + 'statements' => ['return'] + ], + 'single_space_around_construct' => true, + 'control_structure_braces' => true, + 'control_structure_continuation_position' => true, + 'declare_parentheses' => true, + 'no_multiple_statements_per_line' => true, + 'braces_position' => true, + 'statement_indentation' => true, + 'cast_spaces' => true, + 'class_definition' => true, + 'concat_space' => [ + 'spacing' => 'none' + ], + 'declare_equal_normalize' => true, + 'declare_strict_types' => true, + 'elseif' => true, + 'encoding' => true, + 'full_opening_tag' => true, + 'fully_qualified_strict_types' => true, // added by Shift + 'function_declaration' => true, + 'type_declaration_spaces' => true, + 'heredoc_to_nowdoc' => true, + 'include' => true, + 'increment_style' => ['style' => 'post'], + 'indentation_type' => true, + 'linebreak_after_opening_tag' => true, + 'line_ending' => true, + 'lowercase_cast' => true, + 'lowercase_keywords' => true, + 'lowercase_static_reference' => true, // added from Symfony + 'magic_method_casing' => true, // added from Symfony + 'magic_constant_casing' => true, + 'method_argument_space' => true, + 'native_function_casing' => true, + 'no_alias_functions' => true, + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'extra', + 'throw', + 'use', + ] + ], + 'class_attributes_separation' => [ + 'elements' => ['trait_import' => 'none'] + ], + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_closing_tag' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => [ + 'use' => 'echo' + ], + 'no_multiline_whitespace_around_double_arrow' => true, + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line' + ], + 'no_short_bool_cast' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_spaces_after_function_name' => true, + 'no_spaces_around_offset' => true, + 'spaces_inside_parentheses' => true, + 'no_trailing_comma_in_singleline' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unreachable_default_argument_value' => true, + 'no_unused_imports' => true, + 'no_useless_return' => true, + 'no_whitespace_before_comma_in_array' => true, + 'no_whitespace_in_blank_line' => true, + 'normalize_index_brace' => true, + 'not_operator_with_successor_space' => true, + 'object_operator_without_whitespace' => true, + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'phpdoc_indent' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_package' => true, + 'phpdoc_no_useless_inheritdoc' => true, + 'phpdoc_scalar' => true, + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_summary' => true, + 'phpdoc_to_comment' => true, + 'phpdoc_trim' => true, + 'phpdoc_types' => true, + 'phpdoc_var_without_name' => true, + 'self_accessor' => true, + 'short_scalar_cast' => true, + 'simplified_null_return' => true, + 'single_blank_line_at_eof' => true, + 'blank_lines_before_namespace' => true, + 'single_class_element_per_statement' => true, + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + 'single_line_comment_style' => [ + 'comment_types' => ['hash'] + ], + 'single_quote' => true, + 'space_after_semicolon' => true, + 'standardize_not_equals' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'ternary_operator_spaces' => true, + 'trailing_comma_in_multiline' => true, + 'trim_array_spaces' => true, + 'unary_operator_spaces' => true, + 'visibility_required' => [ + 'elements' => ['method', 'property'] + ], + 'whitespace_after_comma_in_array' => true, +]; + +$finder = Finder::create() + ->in('src') + ->name('*.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new Config()) + ->setFinder($finder) + ->setRules($rules) + ->setRiskyAllowed(true) + ->setUsingCache(true); \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7e2776f --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "woeler/komoot-php", + "description": "A PHP library to interact with Komoot", + "type": "library", + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.54", + "symfony/var-dumper": "^7.0" + }, + "license": "GPL-3.0-or-later", + "autoload": { + "psr-4": { + "Woeler\\KomootPhp\\": "src/" + } + }, + "authors": [ + { + "name": "Woeler", + "email": "github@woeler.eu" + } + ], + "require": { + "guzzlehttp/guzzle": "^7.8" + }, + "scripts": { + "cgl": [ + "vendor/bin/php-cs-fixer fix --config .php-cs-fixer.php" + ], + "cgl-ci": [ + "vendor/bin/php-cs-fixer fix --config .php-cs-fixer.php -v --dry-run --diff" + ] + } +} diff --git a/src/Api/Komoot.php b/src/Api/Komoot.php new file mode 100644 index 0000000..ecce42a --- /dev/null +++ b/src/Api/Komoot.php @@ -0,0 +1,164 @@ +client = $client ?? new Client(); + $this->cookies = new CookieJar(); + } + + public function login(): void + { + $response = $this->client->post('https://account.komoot.com/v1/signin', [ + 'json' => ['email' => $this->email, 'password' => $this->password, 'reason' => 'header'], + ]); + + $headerSetCookies = $response->getHeader('Set-Cookie'); + + foreach ($headerSetCookies as $header) { + $this->cookies->setCookie(SetCookie::fromString($header)); + } + + $this->setAccountId(); + + $response = $this->client->get('https://account.komoot.com/actions/transfer?type=signin&reason=header', [ + 'cookies' => $this->cookies, + ]); + + $headerSetCookies = $response->getHeader('Set-Cookie'); + + foreach ($headerSetCookies as $header) { + $this->cookies->setCookie(SetCookie::fromString($header)); + } + } + + private function setAccountId(): void + { + $response = json_decode($this->client->get('https://account.komoot.com/api/account/v1/session?hl=en', [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + + $this->userid = (int) $response['_embedded']['profile']['username']; + } + + public function getTours(int $page = 0, int $limit = 50): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/users/'.$this->userid.'/activities/?page='.$page.'&limit='.$limit, [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function getAllTours(): array + { + $page = 0; + $all = []; + while (true) { + $tours = $this->getTours($page, 50); + $all = array_merge($all, $tours['_embedded']['items']); + + if ($page + 1 === $tours['page']['totalPages']) { + break; + } + $page++; + } + + return $all; + } + + public function getTour(int $tourId): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/tours/'.$tourId, [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function getTourGpx(int $tourId): string + { + return $this->client->get('https://www.komoot.com/api/v007/tours/'.$tourId.'.gpx', [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(); + } + + public function renameTour(int $tourId, string $name): array + { + return json_decode($this->client->patch('https://www.komoot.com/api/v007/tours/'.$tourId, [ + 'cookies' => $this->cookies, + 'json' => ['name' => $name], + ])->getBody()->getContents(), true); + } + + public function changeTourPrivacy(int $tourId, PrivacySetting $privacy): array + { + return json_decode($this->client->patch('https://www.komoot.com/api/v007/tours/'.$tourId, [ + 'cookies' => $this->cookies, + 'json' => ['status' => $privacy->value], + ])->getBody()->getContents(), true); + } + + public function changeSport(int $tourId, Sport $sport): array + { + return json_decode($this->client->patch('https://www.komoot.com/api/v007/tours/'.$tourId, [ + 'cookies' => $this->cookies, + 'json' => ['sport' => $sport->value], + ])->getBody()->getContents(), true); + } + + public function getUser(int $userId): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/users/'.$userId, [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function getSelfUser(): array + { + return $this->getUser($this->userid); + } + + public function getCollections(int $page = 0, int $limit = 50): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/users/'.$this->userid.'/collections/?page='.$page.'&limit='.$limit, [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function getCollection(int $collectionId): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/collections/'.$collectionId, [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function getCollectionTours(int $collectionId): array + { + return json_decode($this->client->get('https://www.komoot.com/api/v007/collections/'.$collectionId.'/compilation/', [ + 'cookies' => $this->cookies, + ])->getBody()->getContents(), true); + } + + public function customRequest(string $endpoint, array $guzzleConfig = [], string $method = 'GET'): array + { + if (! in_array(strtolower($method), ['get', 'delete', 'head', 'options', 'patch', 'post', 'put'])) { + throw new \InvalidArgumentException('Invalid HTTP method given', 1714987334); + } + + return json_decode($this->client->{strtolower($method)}($endpoint, [ + 'cookies' => $this->cookies, + ] + $guzzleConfig)->getBody()->getContents(), true); + } +} diff --git a/src/Enums/PrivacySetting.php b/src/Enums/PrivacySetting.php new file mode 100644 index 0000000..09ad851 --- /dev/null +++ b/src/Enums/PrivacySetting.php @@ -0,0 +1,12 @@ +