mirror of
https://github.com/Sysbot-org/tgscraper.git
synced 2024-11-13 06:59:26 +01:00
CLI refactoring, Postman support, breaking changes
This commit is contained in:
parent
4bf53de7cd
commit
ce4836623b
123
bin/tgscraper
123
bin/tgscraper
@ -1,121 +1,22 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Symfony\Component\Console\Application;
|
||||
use TgScraper\Commands\CreateStubsCommand;
|
||||
use TgScraper\Commands\ExportSchemaCommand;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use TgScraper\Generator;
|
||||
$application = new Application('TGScraper', InstalledVersions::getVersion('sysbot/tgscraper'));
|
||||
|
||||
$args = getopt('o:u:n:hs:');
|
||||
|
||||
|
||||
if (array_key_exists('h', $args)) {
|
||||
echo 'Usage: tgscraper -o <output> [-u <url>] [-n <namespace>] [-s <scheme>]' . PHP_EOL . PHP_EOL;
|
||||
echo 'Options:' . PHP_EOL;
|
||||
echo ' -n <namespace> namespace to use (for PHP stubs only)' . PHP_EOL;
|
||||
echo ' -o <output> output file/directory (the directory must exist) for JSON/YAML schema or PHP stubs' . PHP_EOL;
|
||||
echo ' -s <scheme> path to custom scheme to use (for PHP stubs only)' . PHP_EOL;
|
||||
echo ' -u <url> URL to use for fetching bot API data' . PHP_EOL . PHP_EOL;
|
||||
echo 'Note: based on the "-o" value, the script will automatically detect whether to output a JSON/YAML schema or the PHP stubs.' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
|
||||
$output = $args['o'] ?? null;
|
||||
$namespace = $args['n'] ?? 'Generated';
|
||||
$url = $args['u'] ?? Generator::BOT_API_URL;
|
||||
$scheme = $args['s'] ?? null;
|
||||
|
||||
if (empty($output)) {
|
||||
fwrite(STDERR, 'ERROR: "-o" option missing!' . PHP_EOL . PHP_EOL);
|
||||
fwrite(STDERR, 'Use "tgscraper -h" for help.' . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!empty($url) and !filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
echo '> WARNING: URL not valid, the default one will be used.' . PHP_EOL;
|
||||
$url = Generator::BOT_API_URL;
|
||||
}
|
||||
|
||||
$generator = new Generator($url);
|
||||
$data = null;
|
||||
if (is_dir($output)) {
|
||||
if (!empty($scheme)) {
|
||||
$data = file_get_contents($scheme);
|
||||
if (false === $data) {
|
||||
echo sprintf('> WARNING: Cannot read data from "%s", URL will be used instead.%s', $scheme, PHP_EOL);
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
try {
|
||||
echo '> Extracting JSON scheme.' . PHP_EOL;
|
||||
$data = $generator->toJson();
|
||||
} catch (Throwable $e) {
|
||||
echo '> ERROR: Unable to extract scheme: ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
echo sprintf('> Loaded data from "%s".%s', $scheme, PHP_EOL);
|
||||
}
|
||||
echo sprintf('> Outputting PHP stubs to %s.%s', realpath($output), PHP_EOL);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$result = $generator->toStubs($output, $namespace, $data);
|
||||
if (!$result) {
|
||||
echo '> ERROR: unable to create stubs.' . PHP_EOL;
|
||||
}
|
||||
exit(!$result);
|
||||
}
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
try {
|
||||
echo '> Extracting JSON scheme.' . PHP_EOL;
|
||||
$data = $generator->toJson();
|
||||
} catch (Throwable $e) {
|
||||
echo '> ERROR: Unable to extract scheme: ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
echo sprintf('> Outputting PHP stubs to %s.%s', realpath($output), PHP_EOL);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$result = $generator->toStubs($output, $namespace, $data);
|
||||
if (!$result) {
|
||||
echo '> ERROR: unable to create stubs.' . PHP_EOL;
|
||||
}
|
||||
exit(!$result);
|
||||
}
|
||||
|
||||
echo sprintf('> Using "%s" as URL.%s', $url, PHP_EOL);
|
||||
touch($output);
|
||||
|
||||
$extension = pathinfo($output, PATHINFO_EXTENSION);
|
||||
|
||||
if (in_array($extension, ['yml', 'yaml'])) {
|
||||
echo sprintf('> Outputting YAML schema to %s.%s', realpath($output), PHP_EOL);
|
||||
|
||||
try {
|
||||
$data = $generator->toYaml();
|
||||
} catch (Throwable $e) {
|
||||
echo '> ERROR: Unable to generate scheme:' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$error = file_put_contents($output, $data) === false;
|
||||
|
||||
if ($error) {
|
||||
echo '> ERROR: unable to write to file.' . PHP_EOL;
|
||||
}
|
||||
|
||||
exit($error);
|
||||
}
|
||||
|
||||
|
||||
echo sprintf('> Outputting JSON schema to %s.%s', realpath($output), PHP_EOL);
|
||||
$application->add(new CreateStubsCommand());
|
||||
$application->add(new ExportSchemaCommand());
|
||||
|
||||
try {
|
||||
$data = $generator->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $e) {
|
||||
echo '> ERROR: Unable to generate scheme:' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
$exitCode = $application->run();
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
|
||||
$error = file_put_contents($output, $data) === false;
|
||||
|
||||
if ($error) {
|
||||
echo '> ERROR: unable to write to file.' . PHP_EOL;
|
||||
}
|
||||
|
||||
exit($error);
|
||||
exit($exitCode ?? 1);
|
||||
|
@ -5,8 +5,11 @@
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"nette/php-generator": "^3.5",
|
||||
"paquettg/php-html-parser": "^3.1",
|
||||
"psr/log": "^1.1",
|
||||
"symfony/console": "^5.3",
|
||||
"symfony/yaml": "^5.3"
|
||||
},
|
||||
"autoload": {
|
||||
|
814
composer.lock
generated
814
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ce91774666a1ea15b98376aed25cee10",
|
||||
"content-hash": "e8ae86c9b854e3496c7ed12c9ea0d6c4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
@ -241,16 +241,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/php-enum",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/php-enum.git",
|
||||
"reference": "46cf3d8498b095bd33727b13fd5707263af99421"
|
||||
"reference": "b942d263c641ddb5190929ff840c68f78713e937"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/46cf3d8498b095bd33727b13fd5707263af99421",
|
||||
"reference": "46cf3d8498b095bd33727b13fd5707263af99421",
|
||||
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
|
||||
"reference": "b942d263c641ddb5190929ff840c68f78713e937",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -260,7 +260,7 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"squizlabs/php_codesniffer": "1.*",
|
||||
"vimeo/psalm": "^4.5.1"
|
||||
"vimeo/psalm": "^4.6.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -285,7 +285,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/php-enum/issues",
|
||||
"source": "https://github.com/myclabs/php-enum/tree/1.8.0"
|
||||
"source": "https://github.com/myclabs/php-enum/tree/1.8.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -297,20 +297,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-15T16:11:48+00:00"
|
||||
"time": "2021-07-05T08:18:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/php-generator",
|
||||
"version": "v3.5.3",
|
||||
"version": "v3.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/php-generator.git",
|
||||
"reference": "119f01a7bd590469cb01b538f20a125a28853626"
|
||||
"reference": "59bb35ed6e8da95854fbf7b7d47dce6156b42915"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/119f01a7bd590469cb01b538f20a125a28853626",
|
||||
"reference": "119f01a7bd590469cb01b538f20a125a28853626",
|
||||
"url": "https://api.github.com/repos/nette/php-generator/zipball/59bb35ed6e8da95854fbf7b7d47dce6156b42915",
|
||||
"reference": "59bb35ed6e8da95854fbf7b7d47dce6156b42915",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -353,7 +353,7 @@
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 7.4 features.",
|
||||
"description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.0 features.",
|
||||
"homepage": "https://nette.org",
|
||||
"keywords": [
|
||||
"code",
|
||||
@ -363,9 +363,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/php-generator/issues",
|
||||
"source": "https://github.com/nette/php-generator/tree/v3.5.3"
|
||||
"source": "https://github.com/nette/php-generator/tree/v3.5.4"
|
||||
},
|
||||
"time": "2021-02-24T18:40:21+00:00"
|
||||
"time": "2021-07-05T12:02:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
@ -689,6 +689,54 @@
|
||||
},
|
||||
"time": "2020-07-07T09:29:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
|
||||
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||
"homepage": "https://github.com/php-fig/container",
|
||||
"keywords": [
|
||||
"PSR-11",
|
||||
"container",
|
||||
"container-interface",
|
||||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-fig/container/issues",
|
||||
"source": "https://github.com/php-fig/container/tree/1.1.1"
|
||||
},
|
||||
"time": "2021-03-05T17:36:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.1",
|
||||
@ -794,6 +842,56 @@
|
||||
},
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/log/tree/1.1.4"
|
||||
},
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
@ -838,6 +936,104 @@
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1",
|
||||
"reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php73": "^1.8",
|
||||
"symfony/polyfill-php80": "^1.15",
|
||||
"symfony/service-contracts": "^1.1|^2",
|
||||
"symfony/string": "^5.1"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<4.4",
|
||||
"symfony/dotenv": "<5.1",
|
||||
"symfony/event-dispatcher": "<4.4",
|
||||
"symfony/lock": "<4.4",
|
||||
"symfony/process": "<4.4"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "^4.4|^5.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0",
|
||||
"symfony/event-dispatcher": "^4.4|^5.0",
|
||||
"symfony/lock": "^4.4|^5.0",
|
||||
"symfony/process": "^4.4|^5.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
"symfony/event-dispatcher": "",
|
||||
"symfony/lock": "",
|
||||
"symfony/process": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Console\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Eases the creation of beautiful and testable command line interfaces",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"cli",
|
||||
"command line",
|
||||
"console",
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-12T09:42:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.4.0",
|
||||
@ -985,17 +1181,586 @@
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v5.3.2",
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "71719ab2409401711d619765aa255f9d352a59b2"
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/71719ab2409401711d619765aa255f9d352a59b2",
|
||||
"reference": "71719ab2409401711d619765aa255f9d352a59b2",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab",
|
||||
"reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's grapheme_* functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"grapheme",
|
||||
"intl",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T09:17:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's Normalizer class and related functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"intl",
|
||||
"normalizer",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
|
||||
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-27T09:27:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php73",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
|
||||
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php73\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
|
||||
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ion Bazan",
|
||||
"email": "ion.bazan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
|
||||
"reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/service-implementation": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.4-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Contracts\\Service\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Generic abstractions related to writing services",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-04-01T10:43:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1",
|
||||
"reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-intl-grapheme": "~1.0",
|
||||
"symfony/polyfill-intl-normalizer": "~1.0",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php80": "~1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/error-handler": "^4.4|^5.0",
|
||||
"symfony/http-client": "^4.4|^5.0",
|
||||
"symfony/translation-contracts": "^1.1|^2",
|
||||
"symfony/var-exporter": "^4.4|^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\String\\": ""
|
||||
},
|
||||
"files": [
|
||||
"Resources/functions.php"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"grapheme",
|
||||
"i18n",
|
||||
"string",
|
||||
"unicode",
|
||||
"utf-8",
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-27T11:44:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v5.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "485c83a2fb5893e2ff21bf4bfc7fdf48b4967229"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/485c83a2fb5893e2ff21bf4bfc7fdf48b4967229",
|
||||
"reference": "485c83a2fb5893e2ff21bf4bfc7fdf48b4967229",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1041,7 +1806,7 @@
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v5.3.2"
|
||||
"source": "https://github.com/symfony/yaml/tree/v5.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1057,7 +1822,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-06T09:51:56+00:00"
|
||||
"time": "2021-06-24T08:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
@ -1068,7 +1833,8 @@
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*"
|
||||
"ext-json": "*",
|
||||
"composer-runtime-api": "^2.0"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
|
96
src/Commands/CreateStubsCommand.php
Normal file
96
src/Commands/CreateStubsCommand.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace TgScraper\Commands;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Logger\ConsoleLogger;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TgScraper\Constants\Versions;
|
||||
use TgScraper\Generator;
|
||||
use Throwable;
|
||||
|
||||
class CreateStubsCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'app:create-stubs';
|
||||
|
||||
protected function validateData(string|false $data): bool
|
||||
{
|
||||
return false !== $data and !empty($data);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDescription('Create stubs from bot API schema.')
|
||||
->setHelp('This command allows you to create class stubs for all types of the Telegram bot API.')
|
||||
->addArgument('destination', InputArgument::REQUIRED, 'Destination directory')
|
||||
->addOption('namespace-prefix', null, InputOption::VALUE_REQUIRED, 'Namespace prefix for stubs', 'TelegramApi')
|
||||
->addOption(
|
||||
'json',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Path to JSON file to use instead of fetching from URL (this option takes precedence over "--layer")'
|
||||
)
|
||||
->addOption(
|
||||
'yaml',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Path to YAML file to use instead of fetching from URL (this option takes precedence over "--layer" and "--json")'
|
||||
)
|
||||
->addOption('layer', 'l', InputOption::VALUE_REQUIRED, 'Bot API version to use', 'latest');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$logger = new ConsoleLogger($output);
|
||||
$url = Versions::getVersionFromText($input->getOption('layer'));
|
||||
$yamlPath = $input->getOption('yaml');
|
||||
if (empty($yamlPath)) {
|
||||
$jsonPath = $input->getOption('json');
|
||||
if (empty($jsonPath)) {
|
||||
$logger->info('Using URL: ' . $url);
|
||||
try {
|
||||
$output->writeln('Fetching data from URL...');
|
||||
$generator = new Generator($logger, $url);
|
||||
} catch (Throwable) {
|
||||
return Command::FAILURE;
|
||||
}
|
||||
} else {
|
||||
$data = file_get_contents($jsonPath);
|
||||
if (!$this->validateData($data)) {
|
||||
$logger->critical('Invalid JSON file provided');
|
||||
return Command::INVALID;
|
||||
}
|
||||
$logger->info('Using JSON schema: ' . $jsonPath);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$generator = Generator::fromJson($logger, $data);
|
||||
}
|
||||
} else {
|
||||
$data = file_get_contents($yamlPath);
|
||||
if (!$this->validateData($data)) {
|
||||
$logger->critical('Invalid YAML file provided');
|
||||
return Command::INVALID;
|
||||
}
|
||||
$logger->info('Using YAML schema: ' . $yamlPath);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$generator = Generator::fromYaml($logger, $data);
|
||||
}
|
||||
try {
|
||||
$output->writeln('Creating stubs...');
|
||||
$generator->toStubs($input->getArgument('destination'), $input->getOption('namespace-prefix'));
|
||||
} catch (Exception) {
|
||||
$logger->critical('Could not create stubs.');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln('Done!');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
74
src/Commands/ExportSchemaCommand.php
Normal file
74
src/Commands/ExportSchemaCommand.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace TgScraper\Commands;
|
||||
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Logger\ConsoleLogger;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TgScraper\Constants\Versions;
|
||||
use TgScraper\Generator;
|
||||
use Throwable;
|
||||
|
||||
class ExportSchemaCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'app:export-schema';
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDescription('Export schema as JSON or YAML.')
|
||||
->setHelp('This command allows you to create a schema for a specific version of the Telegram bot API.')
|
||||
->addArgument('destination', InputArgument::REQUIRED, 'Destination file')
|
||||
->addOption(
|
||||
'yaml',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Export schema as YAML instead of JSON (this option takes precedence over "--postman")'
|
||||
)
|
||||
->addOption('postman', null, InputOption::VALUE_NONE, 'Export schema as a Postman-compatible JSON')
|
||||
->addOption('options', 'o', InputOption::VALUE_REQUIRED, 'Encoder options', 0)
|
||||
->addOption('readable', 'r', InputOption::VALUE_NONE, '(JSON only) Generate a human-readable JSON')
|
||||
->addOption('inline', null, InputOption::VALUE_REQUIRED, '(YAML only) Inline level', 6)
|
||||
->addOption('indent', null, InputOption::VALUE_REQUIRED, '(YAML only) Indent level', 4)
|
||||
->addOption('layer', 'l', InputOption::VALUE_REQUIRED, 'Bot API version to use', 'latest');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$logger = new ConsoleLogger($output);
|
||||
$url = Versions::getVersionFromText($input->getOption('layer'));
|
||||
$logger->info('Using URL: ' . $url);
|
||||
try {
|
||||
$output->writeln('Fetching data from URL...');
|
||||
$generator = new Generator($logger, $url);
|
||||
} catch (Throwable) {
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln('Exporting schema from data...');
|
||||
$options = $input->getOption('options');
|
||||
$useYaml = $input->getOption('yaml');
|
||||
if ($useYaml) {
|
||||
$data = $generator->toYaml($input->getOption('inline'), $input->getOption('indent'), $options);
|
||||
}
|
||||
$destination = $input->getArgument('destination');
|
||||
if ($input->getOption('readable')) {
|
||||
$options |= JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
|
||||
}
|
||||
$data ??= $input->getOption('postman') ? $generator->toPostman($options) : $generator->toJson($options);
|
||||
$output->writeln('Saving scheme to file...');
|
||||
$result = file_put_contents($destination, $data);
|
||||
if (false === $result) {
|
||||
$logger->critical('Unable to save file to ' . $destination);
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln('Done!');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
271
src/Common/SchemaExtractor.php
Normal file
271
src/Common/SchemaExtractor.php
Normal file
@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace TgScraper\Common;
|
||||
|
||||
|
||||
use PHPHtmlParser\Dom;
|
||||
use PHPHtmlParser\Exceptions\ChildNotFoundException;
|
||||
use PHPHtmlParser\Exceptions\CircularException;
|
||||
use PHPHtmlParser\Exceptions\ContentLengthException;
|
||||
use PHPHtmlParser\Exceptions\LogicalException;
|
||||
use PHPHtmlParser\Exceptions\NotLoadedException;
|
||||
use PHPHtmlParser\Exceptions\ParentNotFoundException;
|
||||
use PHPHtmlParser\Exceptions\StrictException;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use TgScraper\Constants\Versions;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class SchemaExtractor
|
||||
* @package TgScraper\Common
|
||||
*/
|
||||
class SchemaExtractor
|
||||
{
|
||||
|
||||
/**
|
||||
* Additional methods with boolean return value.
|
||||
*/
|
||||
private const BOOL_RETURNS = [
|
||||
'answerShippingQuery',
|
||||
'answerPreCheckoutQuery'
|
||||
];
|
||||
|
||||
/**
|
||||
* SchemaExtractor constructor.
|
||||
* @param LoggerInterface $logger
|
||||
* @param string $url
|
||||
*/
|
||||
public function __construct(private LoggerInterface $logger, private string $url = Versions::LATEST)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function extract(): array
|
||||
{
|
||||
$dom = new Dom;
|
||||
try {
|
||||
$dom->loadFromURL($this->url);
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage()));
|
||||
throw $e;
|
||||
}
|
||||
try {
|
||||
$elements = $dom->find('h4');
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->critical(sprintf('Unable to load data from URL "%s": %s', $this->url, $e->getMessage()));
|
||||
throw $e;
|
||||
}
|
||||
$data = [];
|
||||
/* @var Dom\Node\AbstractNode $element */
|
||||
foreach ($elements as $element) {
|
||||
if (!str_contains($name = $element->text, ' ')) {
|
||||
$isMethod = lcfirst($name) == $name;
|
||||
$path = $isMethod ? 'methods' : 'types';
|
||||
$temp = $element;
|
||||
$description = '';
|
||||
$table = null;
|
||||
while (true) {
|
||||
try {
|
||||
$element = $element->nextSibling();
|
||||
} catch (ChildNotFoundException) {
|
||||
break;
|
||||
}
|
||||
$tag = $element->tag->name() ?? null;
|
||||
if (empty($temp->text()) or empty($tag) or $tag == 'text') {
|
||||
continue;
|
||||
} elseif (str_starts_with($tag, 'h')) {
|
||||
break;
|
||||
} elseif ($tag == 'p') {
|
||||
$description .= PHP_EOL . $element->innerHtml();
|
||||
} elseif ($tag == 'table') {
|
||||
$table = $element->find('tbody')->find('tr');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* @var Dom\Node\AbstractNode $element */
|
||||
$data[$path][] = self::generateElement(
|
||||
$name,
|
||||
trim($description),
|
||||
$table,
|
||||
$isMethod
|
||||
);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @param Dom\Node\Collection|null $unparsedFields
|
||||
* @param bool $isMethod
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
*/
|
||||
private static function generateElement(
|
||||
string $name,
|
||||
string $description,
|
||||
?Dom\Node\Collection $unparsedFields,
|
||||
bool $isMethod
|
||||
): array {
|
||||
$fields = self::parseFields($unparsedFields, $isMethod);
|
||||
$result = [
|
||||
'name' => $name,
|
||||
'description' => htmlspecialchars_decode(strip_tags($description), ENT_QUOTES),
|
||||
'fields' => $fields
|
||||
];
|
||||
if ($isMethod) {
|
||||
$returnTypes = self::parseReturnTypes($description);
|
||||
if (empty($returnTypes) and in_array($name, self::BOOL_RETURNS)) {
|
||||
$returnTypes[] = 'bool';
|
||||
}
|
||||
$result['return_types'] = $returnTypes;
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dom\Node\Collection|null $fields
|
||||
* @param bool $isMethod
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws NotLoadedException
|
||||
*/
|
||||
private static function parseFields(?Dom\Node\Collection $fields, bool $isMethod): array
|
||||
{
|
||||
$parsedFields = [];
|
||||
$fields = $fields ?? [];
|
||||
foreach ($fields as $field) {
|
||||
/* @var Dom $field */
|
||||
$fieldData = $field->find('td');
|
||||
$name = $fieldData[0]->text;
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
$parsedData = [
|
||||
'name' => $name,
|
||||
'type' => strip_tags($fieldData[1]->innerHtml)
|
||||
];
|
||||
$parsedData['types'] = self::parseFieldTypes($parsedData['type']);
|
||||
unset($parsedData['type']);
|
||||
if ($isMethod) {
|
||||
$parsedData['required'] = $fieldData[2]->text == 'Yes';
|
||||
$parsedData['description'] = htmlspecialchars_decode(
|
||||
strip_tags($fieldData[3]->innerHtml ?? $fieldData[3]->text ?? ''),
|
||||
ENT_QUOTES
|
||||
);
|
||||
} else {
|
||||
$description = htmlspecialchars_decode(strip_tags($fieldData[2]->innerHtml), ENT_QUOTES);
|
||||
$parsedData['optional'] = str_starts_with($description, 'Optional.');
|
||||
$parsedData['description'] = $description;
|
||||
}
|
||||
$parsedFields[] = $parsedData;
|
||||
}
|
||||
return $parsedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rawType
|
||||
* @return array
|
||||
*/
|
||||
private static function parseFieldTypes(string $rawType): array
|
||||
{
|
||||
$types = [];
|
||||
foreach (explode(' or ', $rawType) as $rawOrType) {
|
||||
if (stripos($rawOrType, 'array') === 0) {
|
||||
$types[] = str_replace(' and', ',', $rawOrType);
|
||||
continue;
|
||||
}
|
||||
foreach (explode(' and ', $rawOrType) as $unparsedType) {
|
||||
$types[] = $unparsedType;
|
||||
}
|
||||
}
|
||||
$parsedTypes = [];
|
||||
foreach ($types as $type) {
|
||||
$type = trim(str_replace(['number', 'of'], '', $type));
|
||||
$multiplesCount = substr_count(strtolower($type), 'array');
|
||||
$parsedType = trim(
|
||||
str_replace(
|
||||
['Array', 'Integer', 'String', 'Boolean', 'Float', 'True'],
|
||||
['', 'int', 'string', 'bool', 'float', 'bool'],
|
||||
$type
|
||||
)
|
||||
);
|
||||
for ($i = 0; $i < $multiplesCount; $i++) {
|
||||
$parsedType = sprintf('Array<%s>', $parsedType);
|
||||
}
|
||||
$parsedTypes[] = $parsedType;
|
||||
}
|
||||
return $parsedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @noinspection PhpUndefinedFieldInspection
|
||||
*/
|
||||
private static function parseReturnTypes(string $description): array
|
||||
{
|
||||
$returnTypes = [];
|
||||
$phrases = explode('.', $description);
|
||||
$phrases = array_filter(
|
||||
$phrases,
|
||||
function ($phrase) {
|
||||
return (false !== stripos($phrase, 'returns') or false !== stripos($phrase, 'is returned'));
|
||||
}
|
||||
);
|
||||
foreach ($phrases as $phrase) {
|
||||
$dom = new Dom;
|
||||
$dom->loadStr($phrase);
|
||||
$a = $dom->find('a');
|
||||
$em = $dom->find('em');
|
||||
foreach ($a as $element) {
|
||||
if ($element->text == 'Messages') {
|
||||
$returnTypes[] = 'Array<Message>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$multiplesCount = substr_count(strtolower($phrase), 'array');
|
||||
$returnType = $element->text;
|
||||
for ($i = 0; $i < $multiplesCount; $i++) {
|
||||
$returnType = sprintf('Array<%s>', $returnType);
|
||||
}
|
||||
$returnTypes[] = $returnType;
|
||||
}
|
||||
foreach ($em as $element) {
|
||||
if (in_array($element->text, ['False', 'force', 'Array'])) {
|
||||
continue;
|
||||
}
|
||||
$type = str_replace(['True', 'Int', 'String'], ['bool', 'int', 'string'], $element->text);
|
||||
$returnTypes[] = $type;
|
||||
}
|
||||
}
|
||||
return $returnTypes;
|
||||
}
|
||||
|
||||
}
|
@ -2,12 +2,11 @@
|
||||
/** @noinspection PhpInternalEntityUsedInspection */
|
||||
|
||||
|
||||
namespace TgScraper;
|
||||
namespace TgScraper\Common;
|
||||
|
||||
|
||||
use InvalidArgumentException;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Nette\PhpGenerator\Helpers;
|
||||
use Nette\PhpGenerator\PhpFile;
|
||||
use Nette\PhpGenerator\PhpNamespace;
|
||||
@ -15,7 +14,7 @@ use Nette\PhpGenerator\Type;
|
||||
|
||||
/**
|
||||
* Class StubCreator
|
||||
* @package TgScraper
|
||||
* @package TgScraper\Common
|
||||
*/
|
||||
class StubCreator
|
||||
{
|
||||
@ -27,10 +26,10 @@ class StubCreator
|
||||
|
||||
/**
|
||||
* StubCreator constructor.
|
||||
* @param array $scheme
|
||||
* @param array $schema
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function __construct(private array $scheme, string $namespace = '')
|
||||
public function __construct(private array $schema, string $namespace = '')
|
||||
{
|
||||
if (str_ends_with($namespace, '\\')) {
|
||||
$namespace = substr($namespace, 0, -1);
|
||||
@ -40,6 +39,9 @@ class StubCreator
|
||||
throw new InvalidArgumentException('Namespace invalid');
|
||||
}
|
||||
}
|
||||
if (!is_array($this->schema['methods']) or !is_array($this->schema['types'])) {
|
||||
throw new InvalidArgumentException('Schema invalid');
|
||||
}
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
@ -53,9 +55,11 @@ class StubCreator
|
||||
* @param PhpNamespace $phpNamespace
|
||||
* @return array
|
||||
*/
|
||||
#[Pure] #[ArrayShape(['types' => "string", 'comments' => "string"])]
|
||||
private function parseFieldTypes(array $fieldTypes, PhpNamespace $phpNamespace): array
|
||||
{
|
||||
#[ArrayShape(['types' => "string", 'comments' => "string"])]
|
||||
private function parseFieldTypes(
|
||||
array $fieldTypes,
|
||||
PhpNamespace $phpNamespace
|
||||
): array {
|
||||
$types = [];
|
||||
$comments = [];
|
||||
foreach ($fieldTypes as $fieldType) {
|
||||
@ -82,8 +86,10 @@ class StubCreator
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape(['types' => "string", 'comments' => "string"])]
|
||||
private function parseApiFieldTypes(array $apiTypes, PhpNamespace $phpNamespace): array
|
||||
{
|
||||
private function parseApiFieldTypes(
|
||||
array $apiTypes,
|
||||
PhpNamespace $phpNamespace
|
||||
): array {
|
||||
$types = [];
|
||||
$comments = [];
|
||||
foreach ($apiTypes as $apiType) {
|
||||
@ -110,8 +116,9 @@ class StubCreator
|
||||
* @return PhpFile[]
|
||||
*/
|
||||
#[ArrayShape(['Response' => "\Nette\PhpGenerator\PhpFile"])]
|
||||
private function generateDefaultTypes(string $namespace): array
|
||||
{
|
||||
private function generateDefaultTypes(
|
||||
string $namespace
|
||||
): array {
|
||||
$file = new PhpFile;
|
||||
$phpNamespace = $file->addNamespace($namespace);
|
||||
$response = $phpNamespace->addClass('Response')
|
||||
@ -146,7 +153,7 @@ class StubCreator
|
||||
{
|
||||
$namespace = $this->namespace . '\\Types';
|
||||
$types = $this->generateDefaultTypes($namespace);
|
||||
foreach ($this->scheme['types'] as $type) {
|
||||
foreach ($this->schema['types'] as $type) {
|
||||
$file = new PhpFile;
|
||||
$phpNamespace = $file->addNamespace($namespace);
|
||||
$typeClass = $phpNamespace->addClass($type['name'])
|
||||
@ -193,7 +200,7 @@ class StubCreator
|
||||
->setType(Type::STRING);
|
||||
$sendRequest->addParameter('args')
|
||||
->setType(Type::ARRAY);
|
||||
foreach ($this->scheme['methods'] as $method) {
|
||||
foreach ($this->schema['methods'] as $method) {
|
||||
$function = $apiClass->addMethod($method['name'])
|
||||
->setPublic()
|
||||
->addBody('$args = get_defined_vars();')
|
57
src/Constants/Versions.php
Normal file
57
src/Constants/Versions.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace TgScraper\Constants;
|
||||
|
||||
|
||||
class Versions
|
||||
{
|
||||
|
||||
public const V100 = 'https://web.archive.org/web/20150714025308/https://core.telegram.org/bots/api/';
|
||||
public const V110 = 'https://web.archive.org/web/20150812125616/https://core.telegram.org/bots/api';
|
||||
public const V140 = 'https://web.archive.org/web/20150909214252/https://core.telegram.org/bots/api';
|
||||
public const V150 = 'https://web.archive.org/web/20150921091215/https://core.telegram.org/bots/api/';
|
||||
public const V160 = 'https://web.archive.org/web/20151023071257/https://core.telegram.org/bots/api';
|
||||
public const V180 = 'https://web.archive.org/web/20160112101045/https://core.telegram.org/bots/api';
|
||||
public const V182 = 'https://web.archive.org/web/20160126005312/https://core.telegram.org/bots/api';
|
||||
public const V183 = 'https://web.archive.org/web/20160305132243/https://core.telegram.org/bots/api';
|
||||
public const V200 = 'https://web.archive.org/web/20160413101342/https://core.telegram.org/bots/api';
|
||||
public const V210 = 'https://web.archive.org/web/20160912130321/https://core.telegram.org/bots/api';
|
||||
public const V211 = self::V210;
|
||||
public const V220 = 'https://web.archive.org/web/20161004150232/https://core.telegram.org/bots/api';
|
||||
public const V230 = 'https://web.archive.org/web/20161124162115/https://core.telegram.org/bots/api';
|
||||
public const V231 = 'https://web.archive.org/web/20161204181811/https://core.telegram.org/bots/api';
|
||||
public const V300 = 'https://web.archive.org/web/20170612094628/https://core.telegram.org/bots/api';
|
||||
public const V310 = 'https://web.archive.org/web/20170703123052/https://core.telegram.org/bots/api';
|
||||
public const V320 = 'https://web.archive.org/web/20170819054238/https://core.telegram.org/bots/api';
|
||||
public const V330 = 'https://web.archive.org/web/20170914060628/https://core.telegram.org/bots/api';
|
||||
public const V350 = 'https://web.archive.org/web/20171201065426/https://core.telegram.org/bots/api';
|
||||
public const V360 = 'https://web.archive.org/web/20180217001114/https://core.telegram.org/bots/api';
|
||||
public const V400 = 'https://web.archive.org/web/20180728174553/https://core.telegram.org/bots/api';
|
||||
public const V410 = 'https://web.archive.org/web/20180828155646/https://core.telegram.org/bots/api';
|
||||
public const V420 = 'https://web.archive.org/web/20190417160652/https://core.telegram.org/bots/api';
|
||||
public const V430 = 'https://web.archive.org/web/20190601122107/https://core.telegram.org/bots/api';
|
||||
public const V440 = 'https://web.archive.org/web/20190731114703/https://core.telegram.org/bots/api';
|
||||
public const V450 = 'https://web.archive.org/web/20200107090812/https://core.telegram.org/bots/api';
|
||||
public const V460 = 'https://web.archive.org/web/20200208225346/https://core.telegram.org/bots/api';
|
||||
public const V470 = 'https://web.archive.org/web/20200401052001/https://core.telegram.org/bots/api';
|
||||
public const V480 = 'https://web.archive.org/web/20200429054924/https://core.telegram.org/bots/api';
|
||||
public const V490 = 'https://web.archive.org/web/20200611131321/https://core.telegram.org/bots/api';
|
||||
public const V500 = 'https://web.archive.org/web/20201104151640/https://core.telegram.org/bots/api';
|
||||
public const V510 = 'https://web.archive.org/web/20210315055600/https://core.telegram.org/bots/api';
|
||||
public const V520 = 'https://web.archive.org/web/20210428145432/https://core.telegram.org/bots/api';
|
||||
public const V530 = 'https://web.archive.org/web/20210626142851/https://core.telegram.org/bots/api';
|
||||
public const LATEST = 'https://core.telegram.org/bots/api';
|
||||
|
||||
|
||||
public static function getVersionFromText(string $text): string
|
||||
{
|
||||
$text = str_replace('.', '', $text);
|
||||
$const = sprintf('%s::V%s', self::class, $text);
|
||||
if (defined($const)) {
|
||||
return constant($const);
|
||||
}
|
||||
return self::LATEST;
|
||||
}
|
||||
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
namespace TgScraper;
|
||||
|
||||
use Exception;
|
||||
use JsonException;
|
||||
use PHPHtmlParser\Dom;
|
||||
use InvalidArgumentException;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use PHPHtmlParser\Exceptions\ChildNotFoundException;
|
||||
use PHPHtmlParser\Exceptions\CircularException;
|
||||
use PHPHtmlParser\Exceptions\ContentLengthException;
|
||||
@ -13,59 +13,126 @@ use PHPHtmlParser\Exceptions\NotLoadedException;
|
||||
use PHPHtmlParser\Exceptions\ParentNotFoundException;
|
||||
use PHPHtmlParser\Exceptions\StrictException;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use TgScraper\Common\SchemaExtractor;
|
||||
use TgScraper\Common\StubCreator;
|
||||
use TgScraper\Constants\Versions;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Generator
|
||||
* @package TgScraper
|
||||
*/
|
||||
class Generator
|
||||
{
|
||||
|
||||
private const BOOL_RETURNS = [
|
||||
'answerShippingQuery',
|
||||
'answerPreCheckoutQuery'
|
||||
];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private array $schema;
|
||||
|
||||
public const BOT_API_URL = 'https://core.telegram.org/bots/api';
|
||||
/**
|
||||
* Generator constructor.
|
||||
* @param LoggerInterface $logger
|
||||
* @param string $url
|
||||
* @param array|null $schema
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(
|
||||
private LoggerInterface $logger,
|
||||
private string $url = Versions::LATEST,
|
||||
?array $schema = null
|
||||
) {
|
||||
if (empty($schema)) {
|
||||
$extractor = new SchemaExtractor($this->logger, $this->url);
|
||||
try {
|
||||
$this->logger->info('Schema not provided, extracting from URL.');
|
||||
$schema = $extractor->extract();
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->critical(
|
||||
'An exception occurred while trying to extract the schema: ' . $e->getMessage()
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
/** @var array $schema */
|
||||
$this->schema = $schema;
|
||||
}
|
||||
|
||||
public function __construct(private string $url = self::BOT_API_URL)
|
||||
/**
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NotLoadedException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function fromYaml(LoggerInterface $logger, string $yaml): self
|
||||
{
|
||||
$data = Yaml::parse($yaml);
|
||||
return new self($logger, schema: $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ChildNotFoundException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws StrictException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NotLoadedException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function fromJson(LoggerInterface $logger, string $json): self
|
||||
{
|
||||
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
|
||||
return new self($logger, schema: $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
* @param string $namespace
|
||||
* @param string|null $scheme
|
||||
* @return bool
|
||||
* @throws ClientExceptionInterface
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function toStubs(string $directory = '', string $namespace = '', string $scheme = null): bool
|
||||
public function toStubs(string $directory = '', string $namespace = 'TelegramApi'): void
|
||||
{
|
||||
try {
|
||||
$directory = self::getTargetDirectory($directory);
|
||||
} catch (Exception $e) {
|
||||
echo 'Unable to use target directory:' . $e->getMessage() . PHP_EOL;
|
||||
return false;
|
||||
$this->logger->critical(
|
||||
'An exception occurred while trying to get the target directory: ' . $e->getMessage()
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
mkdir($directory . '/Types', 0755);
|
||||
try {
|
||||
if (!empty($scheme)) {
|
||||
try {
|
||||
$data = json_decode($scheme, true, flags: JSON_THROW_ON_ERROR);
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (JsonException) {
|
||||
$data = null;
|
||||
}
|
||||
}
|
||||
$data = $data ?? $this->extractScheme();
|
||||
$creator = new StubCreator($data, $namespace);
|
||||
$code = $creator->generateCode();
|
||||
foreach ($code['types'] as $className => $type) {
|
||||
$filename = sprintf('%s/Types/%s.php', $directory, $className);
|
||||
file_put_contents($filename, $type);
|
||||
}
|
||||
file_put_contents($directory . '/API.php', $code['api']);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
return false;
|
||||
$creator = new StubCreator($this->schema, $namespace);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->logger->critical(
|
||||
'An exception occurred while trying to parse the schema: ' . $e->getMessage()
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
return true;
|
||||
$code = $creator->generateCode();
|
||||
foreach ($code['types'] as $className => $type) {
|
||||
$this->logger->info('Generating class for Type: ' . $className);
|
||||
$filename = sprintf('%s/Types/%s.php', $directory, $className);
|
||||
file_put_contents($filename, $type);
|
||||
}
|
||||
file_put_contents($directory . '/API.php', $code['api']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,8 +142,8 @@ class Generator
|
||||
*/
|
||||
private static function getTargetDirectory(string $path): string
|
||||
{
|
||||
$path = realpath($path);
|
||||
if (false === $path) {
|
||||
$result = realpath($path);
|
||||
if (false === $result) {
|
||||
if (!mkdir($path)) {
|
||||
$path = __DIR__ . '/../generated';
|
||||
if (!file_exists($path)) {
|
||||
@ -84,268 +151,97 @@ class Generator
|
||||
}
|
||||
}
|
||||
}
|
||||
if (realpath($path) === false) {
|
||||
$result = realpath($path);
|
||||
if (false === $result) {
|
||||
throw new Exception('Could not create target directory');
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
* @throws ClientExceptionInterface
|
||||
*/
|
||||
private function extractScheme(): array
|
||||
{
|
||||
$dom = new Dom;
|
||||
$dom->loadFromURL($this->url);
|
||||
$elements = $dom->find('h4');
|
||||
$data = [];
|
||||
/* @var Dom\Node\AbstractNode $element */
|
||||
foreach ($elements as $element) {
|
||||
if (!str_contains($name = $element->text, ' ')) {
|
||||
$isMethod = self::isMethod($name);
|
||||
$path = $isMethod ? 'methods' : 'types';
|
||||
$temp = $element;
|
||||
$description = '';
|
||||
$table = null;
|
||||
while (true) {
|
||||
try {
|
||||
$element = $element->nextSibling();
|
||||
} catch (ChildNotFoundException) {
|
||||
break;
|
||||
}
|
||||
$tag = $element->tag->name() ?? null;
|
||||
if (empty($temp->text()) or empty($tag) or $tag == 'text') {
|
||||
continue;
|
||||
} elseif (str_starts_with($tag, 'h')) {
|
||||
break;
|
||||
} elseif ($tag == 'p') {
|
||||
$description .= PHP_EOL . $element->innerHtml();
|
||||
} elseif ($tag == 'table') {
|
||||
$table = $element->find('tbody')->find('tr');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* @var Dom\Node\AbstractNode $element */
|
||||
$data[$path][] = self::generateElement(
|
||||
$name,
|
||||
trim($description),
|
||||
$table,
|
||||
$isMethod
|
||||
);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function isMethod(string $name): bool
|
||||
{
|
||||
return lcfirst($name) == $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @param Dom\Node\Collection|null $unparsedFields
|
||||
* @param bool $isMethod
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
*/
|
||||
private static function generateElement(
|
||||
string $name,
|
||||
string $description,
|
||||
?Dom\Node\Collection $unparsedFields,
|
||||
bool $isMethod
|
||||
): array {
|
||||
$fields = self::parseFields($unparsedFields, $isMethod);
|
||||
if (!$isMethod) {
|
||||
return [
|
||||
'name' => $name,
|
||||
'description' => htmlspecialchars_decode(strip_tags($description), ENT_QUOTES),
|
||||
'fields' => $fields
|
||||
];
|
||||
}
|
||||
$returnTypes = self::parseReturnTypes($description);
|
||||
if (empty($returnTypes) and in_array($name, self::BOOL_RETURNS)) {
|
||||
$returnTypes[] = 'bool';
|
||||
}
|
||||
return [
|
||||
'name' => $name,
|
||||
'description' => htmlspecialchars_decode(strip_tags($description), ENT_QUOTES),
|
||||
'fields' => $fields,
|
||||
'return_types' => $returnTypes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dom\Node\Collection|null $fields
|
||||
* @param bool $isMethod
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws NotLoadedException
|
||||
*/
|
||||
private static function parseFields(?Dom\Node\Collection $fields, bool $isMethod): array
|
||||
{
|
||||
$parsedFields = [];
|
||||
$fields = $fields ?? [];
|
||||
foreach ($fields as $field) {
|
||||
/* @var Dom $field */
|
||||
$fieldData = $field->find('td');
|
||||
$name = $fieldData[0]->text;
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
$parsedData = [
|
||||
'name' => $name,
|
||||
'type' => strip_tags($fieldData[1]->innerHtml)
|
||||
];
|
||||
$parsedData['types'] = self::parseFieldTypes($parsedData['type']);
|
||||
unset($parsedData['type']);
|
||||
if ($isMethod) {
|
||||
$parsedData['required'] = $fieldData[2]->text == 'Yes';
|
||||
$parsedData['description'] = htmlspecialchars_decode(
|
||||
strip_tags($fieldData[3]->innerHtml ?? $fieldData[3]->text ?? ''),
|
||||
ENT_QUOTES
|
||||
);
|
||||
} else {
|
||||
$description = htmlspecialchars_decode(strip_tags($fieldData[2]->innerHtml), ENT_QUOTES);
|
||||
$parsedData['optional'] = str_starts_with($description, 'Optional.');
|
||||
$parsedData['description'] = $description;
|
||||
}
|
||||
$parsedFields[] = $parsedData;
|
||||
}
|
||||
return $parsedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rawType
|
||||
* @return array
|
||||
*/
|
||||
private static function parseFieldTypes(string $rawType): array
|
||||
{
|
||||
$types = [];
|
||||
foreach (explode(' or ', $rawType) as $rawOrType) {
|
||||
if (stripos($rawOrType, 'array') === 0) {
|
||||
$types[] = str_replace(' and', ',', $rawOrType);
|
||||
continue;
|
||||
}
|
||||
foreach (explode(' and ', $rawOrType) as $unparsedType) {
|
||||
$types[] = $unparsedType;
|
||||
}
|
||||
}
|
||||
$parsedTypes = [];
|
||||
foreach ($types as $type) {
|
||||
$type = trim(str_replace(['number', 'of'], '', $type));
|
||||
$multiplesCount = substr_count(strtolower($type), 'array');
|
||||
$parsedType = trim(
|
||||
str_replace(
|
||||
['Array', 'Integer', 'String', 'Boolean', 'Float', 'True'],
|
||||
['', 'int', 'string', 'bool', 'float', 'bool'],
|
||||
$type
|
||||
)
|
||||
);
|
||||
for ($i = 0; $i < $multiplesCount; $i++) {
|
||||
$parsedType = sprintf('Array<%s>', $parsedType);
|
||||
}
|
||||
$parsedTypes[] = $parsedType;
|
||||
}
|
||||
return $parsedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $description
|
||||
* @return array
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws NotLoadedException
|
||||
* @throws StrictException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @noinspection PhpUndefinedFieldInspection
|
||||
*/
|
||||
private static function parseReturnTypes(string $description): array
|
||||
{
|
||||
$returnTypes = [];
|
||||
$phrases = explode('.', $description);
|
||||
$phrases = array_filter(
|
||||
$phrases,
|
||||
function ($phrase) {
|
||||
return (false !== stripos($phrase, 'returns') or false !== stripos($phrase, 'is returned'));
|
||||
}
|
||||
);
|
||||
foreach ($phrases as $phrase) {
|
||||
$dom = new Dom;
|
||||
$dom->loadStr($phrase);
|
||||
$a = $dom->find('a');
|
||||
$em = $dom->find('em');
|
||||
foreach ($a as $element) {
|
||||
if ($element->text == 'Messages') {
|
||||
$returnTypes[] = 'Array<Message>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$multiplesCount = substr_count(strtolower($phrase), 'array');
|
||||
$returnType = $element->text;
|
||||
for ($i = 0; $i < $multiplesCount; $i++) {
|
||||
$returnType = sprintf('Array<%s>', $returnType);
|
||||
}
|
||||
$returnTypes[] = $returnType;
|
||||
}
|
||||
foreach ($em as $element) {
|
||||
if (in_array($element->text, ['False', 'force', 'Array'])) {
|
||||
continue;
|
||||
}
|
||||
$type = str_replace(['True', 'Int', 'String'], ['bool', 'int', 'string'], $element->text);
|
||||
$returnTypes[] = $type;
|
||||
}
|
||||
}
|
||||
return $returnTypes;
|
||||
@mkdir($result . '/Types', 0755);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $options
|
||||
* @return string
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @throws NotLoadedException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
*/
|
||||
public function toJson(int $options = 0): string
|
||||
{
|
||||
$scheme = $this->extractScheme();
|
||||
return json_encode($scheme, $options);
|
||||
return json_encode($this->schema, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ChildNotFoundException
|
||||
* @throws CircularException
|
||||
* @throws ParentNotFoundException
|
||||
* @throws StrictException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NotLoadedException
|
||||
* @throws ContentLengthException
|
||||
* @throws LogicalException
|
||||
* @param int $inline
|
||||
* @param int $indent
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function toYaml(int $inline = 6, int $indent = 4, int $flags = 0): string
|
||||
{
|
||||
$scheme = $this->extractScheme();
|
||||
return Yaml::dump($scheme, $inline, $indent, $flags);
|
||||
return Yaml::dump($this->schema, $inline, $indent, $flags);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thanks to davtur19 (https://github.com/davtur19/TuriBotGen/blob/master/postman.php)
|
||||
* @param int $options
|
||||
* @return string
|
||||
*/
|
||||
#[ArrayShape(['info' => "string[]", 'variable' => "string[]", 'item' => "array[]"])]
|
||||
public function toPostman(
|
||||
int $options = 0
|
||||
): string {
|
||||
$result = [
|
||||
'info' => [
|
||||
'name' => 'Telegram Bot API',
|
||||
'schema' => 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
|
||||
],
|
||||
'variable' => [
|
||||
'key' => 'token',
|
||||
'value' => '1234:AAbbcc',
|
||||
'type' => 'string'
|
||||
]
|
||||
];
|
||||
foreach ($this->schema['methods'] as $method) {
|
||||
$formData = [];
|
||||
if (!empty($method['fields'])) {
|
||||
foreach ($method['fields'] as $field) {
|
||||
$formData[] = [
|
||||
'key' => $field['name'],
|
||||
'value' => '',
|
||||
'description' => sprintf(
|
||||
'%s. %s',
|
||||
$field['required'] ? 'Required' : 'Optional',
|
||||
$field['description']
|
||||
),
|
||||
'type' => 'text'
|
||||
];
|
||||
}
|
||||
}
|
||||
$result['item'][] = [
|
||||
'name' => $method['name'],
|
||||
'request' => [
|
||||
'method' => 'POST',
|
||||
'body' => [
|
||||
'mode' => 'formdata',
|
||||
'formdata' => $formData
|
||||
],
|
||||
'url' => [
|
||||
'raw' => 'https://api.telegram.org/bot{{token}}/' . $method['name'],
|
||||
'protocol' => 'https',
|
||||
'host' => [
|
||||
'api',
|
||||
'telegram',
|
||||
'org'
|
||||
],
|
||||
'path' => [
|
||||
'bot{{token}}',
|
||||
$method['name']
|
||||
]
|
||||
],
|
||||
'description' => $method['description']
|
||||
]
|
||||
];
|
||||
}
|
||||
return json_encode($result, $options);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user