parent
f860ca0287
commit
52903d216a
29 changed files with 1917 additions and 9 deletions
|
@ -5,6 +5,8 @@
|
|||
}
|
||||
},
|
||||
"require": {
|
||||
"flashwave/index": "^0.2408.40014"
|
||||
"flashwave/index": "^0.2408.40014",
|
||||
"flashwave/syokuhou": "^1.2",
|
||||
"sentry/sdk": "^4.0"
|
||||
}
|
||||
}
|
||||
|
|
701
composer.lock
generated
701
composer.lock
generated
|
@ -4,15 +4,15 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d5cb22679bb951d16ca033d549b45f20",
|
||||
"content-hash": "25cd4c9695654bac1ffda2ab6d6033de",
|
||||
"packages": [
|
||||
{
|
||||
"name": "flashwave/index",
|
||||
"version": "v0.2408.182001",
|
||||
"version": "v0.2408.40014",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://patchii.net/flash/index.git",
|
||||
"reference": "5a1fdcccedf818897a3468d5457875fabfb2ce28"
|
||||
"reference": "fbca708fbd75e8ecc6b36b39c1307a67bf250808"
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
|
@ -46,7 +46,700 @@
|
|||
],
|
||||
"description": "Composer package for the common library for my projects.",
|
||||
"homepage": "https://railgun.sh/index",
|
||||
"time": "2024-08-18T20:01:21+00:00"
|
||||
"time": "2024-08-04T00:14:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flashwave/syokuhou",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://patchii.net/flash/syokuhou.git",
|
||||
"reference": "129a46c0d917382f9bc195cce278be51984eb87d"
|
||||
},
|
||||
"require": {
|
||||
"flashwave/index": "^0.2408.40014",
|
||||
"php": ">=8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.11",
|
||||
"phpunit/phpunit": "^11.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Syokuhou\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"bsd-3-clause-clear"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "flashwave",
|
||||
"email": "packagist@flash.moe",
|
||||
"homepage": "https://flash.moe",
|
||||
"role": "mom"
|
||||
}
|
||||
],
|
||||
"description": "Configuration library for PHP.",
|
||||
"homepage": "https://railgun.sh/syokuhou",
|
||||
"time": "2024-08-04T01:07:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"ralouphie/getallheaders": "^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"http-interop/http-factory-tests": "0.9.0",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://sagikazarmark.hu"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-18T11:15:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
|
||||
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.0.0",
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.4",
|
||||
"vimeo/psalm": "^4.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jean85\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alessandro Lai",
|
||||
"email": "alessandro.lai85@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to get pretty versions strings of installed dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"package",
|
||||
"release",
|
||||
"versions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
|
||||
},
|
||||
"time": "2024-03-08T09:58:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "src"
|
||||
}
|
||||
},
|
||||
"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/3.0.0"
|
||||
},
|
||||
"time": "2021-07-14T16:46:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^5 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"support": {
|
||||
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sdk",
|
||||
"version": "4.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-php-sdk.git",
|
||||
"reference": "fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53",
|
||||
"reference": "fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"sentry/sentry": "^4.0"
|
||||
},
|
||||
"type": "metapackage",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "This is a meta package of sentry/sentry. We recommend using sentry/sentry directly.",
|
||||
"homepage": "http://sentry.io",
|
||||
"keywords": [
|
||||
"crash-reporting",
|
||||
"crash-reports",
|
||||
"error-handler",
|
||||
"error-monitoring",
|
||||
"log",
|
||||
"logging",
|
||||
"sentry"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-php-sdk/issues",
|
||||
"source": "https://github.com/getsentry/sentry-php-sdk/tree/4.0.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-06T10:23:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sentry",
|
||||
"version": "4.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-php.git",
|
||||
"reference": "61770efd8b7888e0bdd7d234f0ba67b066e47d04"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/61770efd8b7888e0bdd7d234f0ba67b066e47d04",
|
||||
"reference": "61770efd8b7888e0bdd7d234f0ba67b066e47d04",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
|
||||
"jean85/pretty-package-versions": "^1.5|^2.0.4",
|
||||
"php": "^7.2|^8.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
"symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"raven/raven": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.4",
|
||||
"guzzlehttp/promises": "^1.0|^2.0",
|
||||
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
|
||||
"monolog/monolog": "^1.6|^2.0|^3.0",
|
||||
"phpbench/phpbench": "^1.0",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpunit/phpunit": "^8.5.14|^9.4",
|
||||
"symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
|
||||
"vimeo/psalm": "^4.17"
|
||||
},
|
||||
"suggest": {
|
||||
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Sentry\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "PHP SDK for Sentry (http://sentry.io)",
|
||||
"homepage": "http://sentry.io",
|
||||
"keywords": [
|
||||
"crash-reporting",
|
||||
"crash-reports",
|
||||
"error-handler",
|
||||
"error-monitoring",
|
||||
"log",
|
||||
"logging",
|
||||
"profiling",
|
||||
"sentry",
|
||||
"tracing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-php/issues",
|
||||
"source": "https://github.com/getsentry/sentry-php/tree/4.8.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-16T13:45:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.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": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v7.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
|
||||
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\OptionsResolver\\": ""
|
||||
},
|
||||
"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": "Provides an improved replacement for the array_replace PHP function",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"config",
|
||||
"configuration",
|
||||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
|
||||
},
|
||||
"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": "2024-05-31T14:57:53+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
25
satori.php
25
satori.php
|
@ -1,11 +1,14 @@
|
|||
<?php
|
||||
namespace Satori;
|
||||
|
||||
use Index\Data\DbTools;
|
||||
use Syokuhou\SharpConfig;
|
||||
|
||||
define('SAT_ROOT', __DIR__);
|
||||
define('SAT_DIR_MIGRATIONS', SAT_ROOT . '/database');
|
||||
define('SAT_DIR_PUBLIC', SAT_ROOT . '/public');
|
||||
define('SAT_DEBUG', is_file(SAT_ROOT . '/.debug'));
|
||||
define('SAT_VERSION', '20240826');
|
||||
define('SAT_VERSION', '20240805');
|
||||
|
||||
require_once SAT_ROOT . '/vendor/autoload.php';
|
||||
|
||||
|
@ -13,4 +16,22 @@ error_reporting(SAT_DEBUG ? -1 : 0);
|
|||
mb_internal_encoding('utf-8');
|
||||
date_default_timezone_set('utc');
|
||||
|
||||
$sat = new SatoriContext;
|
||||
$cfg = SharpConfig::fromFile(SAT_ROOT . '/config.cfg');
|
||||
|
||||
if($cfg->hasValues('sentry:dsn'))
|
||||
(function($cfg) {
|
||||
\Sentry\init([
|
||||
'dsn' => $cfg->getString('dsn'),
|
||||
'traces_sample_rate' => $cfg->getFloat('tracesRate', 0.2),
|
||||
'profiles_sample_rate' => $cfg->getFloat('profilesRate', 0.2),
|
||||
]);
|
||||
|
||||
set_exception_handler(function(\Throwable $ex) {
|
||||
\Sentry\captureException($ex);
|
||||
});
|
||||
})($cfg->scopeTo('sentry'));
|
||||
|
||||
$dbc = DbTools::create($cfg->getString('database:dsn', 'null'));
|
||||
$dbc->execute('SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';');
|
||||
|
||||
$sat = new SatoriContext($cfg, $dbc);
|
||||
|
|
30
src/Booru/BooruContext.php
Normal file
30
src/Booru/BooruContext.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
use Syokuhou\IConfig;
|
||||
|
||||
class BooruContext {
|
||||
private array $sources = [];
|
||||
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->sources[] = new Danbooru\DanbooruSource($config->scopeTo('danbooru'));
|
||||
$this->sources[] = new Gelbooru\GelbooruSource($config->scopeTo('gelbooru'));
|
||||
$this->sources[] = new Konachan\KonachanSource($config->scopeTo('konachan'));
|
||||
$this->sources[] = new Yandere\YandereSource($config->scopeTo('yandere'));
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getSources(): array {
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
public function getSourceByName(string $name): ?IBooruSource {
|
||||
foreach($this->sources as $source)
|
||||
if($source->getInfo()->getName() === $name)
|
||||
return $source;
|
||||
return null;
|
||||
}
|
||||
}
|
16
src/Booru/BooruErrorResponse.php
Normal file
16
src/Booru/BooruErrorResponse.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
class BooruErrorResponse implements \JsonSerializable {
|
||||
public function __construct(
|
||||
private string $code
|
||||
) {}
|
||||
|
||||
public function getCode(): string {
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed {
|
||||
return ['error' => $this->code];
|
||||
}
|
||||
}
|
24
src/Booru/BooruQueryResponse.php
Normal file
24
src/Booru/BooruQueryResponse.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
class BooruQueryResponse implements \JsonSerializable {
|
||||
public function __construct(
|
||||
private BooruSourceInfo $source,
|
||||
private array $posts
|
||||
) {}
|
||||
|
||||
public function getSource(): BooruSourceInfo {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function getPosts(): array {
|
||||
return $this->posts;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed {
|
||||
return [
|
||||
'source' => $this->source,
|
||||
'posts' => $this->posts,
|
||||
];
|
||||
}
|
||||
}
|
59
src/Booru/BooruRoutes.php
Normal file
59
src/Booru/BooruRoutes.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
use stdClass;
|
||||
use Index\XArray;
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
||||
|
||||
class BooruRoutes extends RouteHandler {
|
||||
public function __construct(
|
||||
private BooruContext $context
|
||||
) {}
|
||||
|
||||
private static function makePostsJsonReady(array $posts): array {
|
||||
return XArray::select($posts, function($post) {
|
||||
$output = new stdClass;
|
||||
|
||||
if($post instanceof IBooruPost) {
|
||||
$output->id = (int)$post->getId();
|
||||
$output->rating = $post->getRating();
|
||||
if($post->hasFileUrl())
|
||||
$output->file_url = $post->getFileUrl();
|
||||
$output->post_url = $post->getPostUrl();
|
||||
$output->tags = $post->getTags();
|
||||
|
||||
if($post instanceof IBooruPostWithSpecificTags) {
|
||||
$output->tags_general = $post->getGeneralTags();
|
||||
$output->tags_character = $post->getCharacterTags();
|
||||
$output->tags_copyright = $post->getCopyrightTags();
|
||||
$output->tags_artist = $post->getArtistTags();
|
||||
$output->tags_meta = $post->getMetaTags();
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
});
|
||||
}
|
||||
|
||||
#[HttpGet('/booru.php')]
|
||||
public function getPHP($response, $request) {
|
||||
$sourceName = (string)$request->getParam('b');
|
||||
$tags = (string)$request->getParam('t');
|
||||
|
||||
if($sourceName === '') {
|
||||
$sources = $this->context->getSources();
|
||||
if(empty($sources))
|
||||
return new BooruErrorResponse('none');
|
||||
$source = $sources[0];
|
||||
} else
|
||||
$source = $this->context->getSourceByName($sourceName);
|
||||
|
||||
if($source === null)
|
||||
return new BooruErrorResponse('booru');
|
||||
|
||||
return new BooruQueryResponse(
|
||||
$source->getInfo(),
|
||||
self::makePostsJsonReady($source->getPosts($tags))
|
||||
);
|
||||
}
|
||||
}
|
25
src/Booru/BooruSourceInfo.php
Normal file
25
src/Booru/BooruSourceInfo.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
class BooruSourceInfo implements \JsonSerializable {
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $title
|
||||
) {}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getTitle(): string {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed {
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'type' => $this->name,
|
||||
'title' => $this->title,
|
||||
];
|
||||
}
|
||||
}
|
52
src/Booru/Danbooru/DanbooruPost.php
Normal file
52
src/Booru/Danbooru/DanbooruPost.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Danbooru;
|
||||
|
||||
use Satori\Booru\IBooruPostWithSpecificTags;
|
||||
|
||||
class DanbooruPost implements IBooruPostWithSpecificTags {
|
||||
public function __construct(private array $info) {}
|
||||
|
||||
public function getId(): string {
|
||||
return (string)$this->info['id'];
|
||||
}
|
||||
|
||||
public function getRating(): string {
|
||||
return $this->info['rating'];
|
||||
}
|
||||
|
||||
public function getPostUrl(): string {
|
||||
return sprintf('https://danbooru.donmai.us/posts/%d', $this->info['id']);
|
||||
}
|
||||
|
||||
public function getTags(): array {
|
||||
return explode(' ', $this->info['tag_string']);
|
||||
}
|
||||
|
||||
public function hasFileUrl(): bool {
|
||||
return array_key_exists('file_url', $this->info);
|
||||
}
|
||||
|
||||
public function getFileUrl(): string {
|
||||
return $this->info['file_url'];
|
||||
}
|
||||
|
||||
public function getGeneralTags(): array {
|
||||
return explode(' ', $this->info['tag_string_general']);
|
||||
}
|
||||
|
||||
public function getCharacterTags(): array {
|
||||
return explode(' ', $this->info['tag_string_character']);
|
||||
}
|
||||
|
||||
public function getCopyrightTags(): array {
|
||||
return explode(' ', $this->info['tag_string_copyright']);
|
||||
}
|
||||
|
||||
public function getArtistTags(): array {
|
||||
return explode(' ', $this->info['tag_string_artist']);
|
||||
}
|
||||
|
||||
public function getMetaTags(): array {
|
||||
return explode(' ', $this->info['tag_string_meta']);
|
||||
}
|
||||
}
|
63
src/Booru/Danbooru/DanbooruSource.php
Normal file
63
src/Booru/Danbooru/DanbooruSource.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Danbooru;
|
||||
|
||||
use Index\XArray;
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\Booru\BooruSourceInfo;
|
||||
use Satori\Booru\IBooruSource;
|
||||
|
||||
class DanbooruSource implements IBooruSource {
|
||||
private const POSTS_URL = 'https://danbooru.donmai.us/posts.json';
|
||||
|
||||
private BooruSourceInfo $info;
|
||||
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->info = new BooruSourceInfo('danbooru', 'Danbooru');
|
||||
}
|
||||
|
||||
public function getInfo(): BooruSourceInfo {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function getPosts(string $tags, bool $random = true, int $limit = 20): array {
|
||||
if($random)
|
||||
$tags .= ' order:random';
|
||||
|
||||
$params = [];
|
||||
if($tags !== '')
|
||||
$params['tags'] = $tags;
|
||||
if($limit > 0)
|
||||
$params['limit'] = $limit;
|
||||
|
||||
$url = self::POSTS_URL;
|
||||
if(!empty($params))
|
||||
$url .= '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
$curl = curl_init($url);
|
||||
|
||||
$headers = ['Accept: application/json'];
|
||||
if($this->config->hasValues('token'))
|
||||
$headers[] = sprintf('Authorization: Basic %s', base64_encode($this->config->getString('token')));
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_CERTINFO => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 5,
|
||||
CURLOPT_PATH_AS_IS => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => sprintf('Satori/%s (+https://fii.moe/satori)', SAT_VERSION),
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
]);
|
||||
$response = json_decode(curl_exec($curl), true);
|
||||
curl_close($curl);
|
||||
|
||||
return empty($response) ? [] : XArray::select($response, fn($post) => new DanbooruPost($post));
|
||||
}
|
||||
}
|
32
src/Booru/Gelbooru/GelbooruPost.php
Normal file
32
src/Booru/Gelbooru/GelbooruPost.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Gelbooru;
|
||||
|
||||
use Satori\Booru\IBooruPost;
|
||||
|
||||
class GelbooruPost implements IBooruPost {
|
||||
public function __construct(private array $info) {}
|
||||
|
||||
public function getId(): string {
|
||||
return (string)$this->info['id'];
|
||||
}
|
||||
|
||||
public function getRating(): string {
|
||||
return $this->info['rating'][0];
|
||||
}
|
||||
|
||||
public function getPostUrl(): string {
|
||||
return sprintf('https://gelbooru.com/index.php?page=post&s=view&id=%d', $this->info['id']);
|
||||
}
|
||||
|
||||
public function getTags(): array {
|
||||
return explode(' ', $this->info['tags']);
|
||||
}
|
||||
|
||||
public function hasFileUrl(): bool {
|
||||
return array_key_exists('file_url', $this->info);
|
||||
}
|
||||
|
||||
public function getFileUrl(): string {
|
||||
return $this->info['file_url'];
|
||||
}
|
||||
}
|
59
src/Booru/Gelbooru/GelbooruSource.php
Normal file
59
src/Booru/Gelbooru/GelbooruSource.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Gelbooru;
|
||||
|
||||
use Index\XArray;
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\Booru\BooruSourceInfo;
|
||||
use Satori\Booru\IBooruSource;
|
||||
|
||||
class GelbooruSource implements IBooruSource {
|
||||
private const POSTS_URL = 'https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1';
|
||||
|
||||
private BooruSourceInfo $info;
|
||||
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->info = new BooruSourceInfo('gelbooru', 'Gelbooru');
|
||||
}
|
||||
|
||||
public function getInfo(): BooruSourceInfo {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function getPosts(string $tags, bool $random = true, int $limit = 20): array {
|
||||
if($random)
|
||||
$tags .= ' sort:random';
|
||||
|
||||
$params = [];
|
||||
if($tags !== '')
|
||||
$params['tags'] = $tags;
|
||||
if($limit > 0)
|
||||
$params['limit'] = $limit;
|
||||
|
||||
$url = self::POSTS_URL;
|
||||
if(!empty($params))
|
||||
$url .= '&' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_CERTINFO => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 5,
|
||||
CURLOPT_PATH_AS_IS => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => sprintf('Satori/%s (+https://fii.moe/satori)', SAT_VERSION),
|
||||
CURLOPT_HTTPHEADER => ['Accept: application/json'],
|
||||
]);
|
||||
$response = json_decode(curl_exec($curl), true);
|
||||
curl_close($curl);
|
||||
|
||||
return empty($response) || empty($response['post']) || !is_array($response['post'])
|
||||
? [] : XArray::select($response['post'], fn($post) => new GelbooruPost($post));
|
||||
}
|
||||
}
|
12
src/Booru/IBooruPost.php
Normal file
12
src/Booru/IBooruPost.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
interface IBooruPost {
|
||||
public function getId(): string;
|
||||
public function getRating(): string;
|
||||
public function getPostUrl(): string;
|
||||
public function getTags(): array;
|
||||
|
||||
public function hasFileUrl(): bool;
|
||||
public function getFileUrl(): string;
|
||||
}
|
10
src/Booru/IBooruPostWithSpecificTags.php
Normal file
10
src/Booru/IBooruPostWithSpecificTags.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
interface IBooruPostWithSpecificTags extends IBooruPost {
|
||||
public function getGeneralTags(): array;
|
||||
public function getCharacterTags(): array;
|
||||
public function getCopyrightTags(): array;
|
||||
public function getArtistTags(): array;
|
||||
public function getMetaTags(): array;
|
||||
}
|
7
src/Booru/IBooruSource.php
Normal file
7
src/Booru/IBooruSource.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace Satori\Booru;
|
||||
|
||||
interface IBooruSource {
|
||||
public function getInfo(): BooruSourceInfo;
|
||||
public function getPosts(string $tags, bool $random = true, int $limit = 20): array;
|
||||
}
|
37
src/Booru/Konachan/KonachanPost.php
Normal file
37
src/Booru/Konachan/KonachanPost.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Konachan;
|
||||
|
||||
use Satori\Booru\IBooruPost;
|
||||
|
||||
class KonachanPost implements IBooruPost {
|
||||
public function __construct(private array $info) {}
|
||||
|
||||
public function getId(): string {
|
||||
return (string)$this->info['id'];
|
||||
}
|
||||
|
||||
public function getRating(): string {
|
||||
return $this->info['rating'];
|
||||
}
|
||||
|
||||
public function isSafe(): bool {
|
||||
return $this->info['rating'] === 'g'
|
||||
|| $this->info['rating'] === 's';
|
||||
}
|
||||
|
||||
public function getPostUrl(): string {
|
||||
return sprintf('https://konachan.%s/post/show/%d', $this->isSafe() ? 'net' : 'com', $this->info['id']);
|
||||
}
|
||||
|
||||
public function getTags(): array {
|
||||
return explode(' ', $this->info['tags']);
|
||||
}
|
||||
|
||||
public function hasFileUrl(): bool {
|
||||
return array_key_exists('file_url', $this->info);
|
||||
}
|
||||
|
||||
public function getFileUrl(): string {
|
||||
return $this->info['file_url'];
|
||||
}
|
||||
}
|
59
src/Booru/Konachan/KonachanSource.php
Normal file
59
src/Booru/Konachan/KonachanSource.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Konachan;
|
||||
|
||||
use Index\XArray;
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\Booru\BooruSourceInfo;
|
||||
use Satori\Booru\IBooruSource;
|
||||
|
||||
class KonachanSource implements IBooruSource {
|
||||
private const POSTS_URL = 'https://konachan.com/post.json?api_version=2';
|
||||
|
||||
private BooruSourceInfo $info;
|
||||
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->info = new BooruSourceInfo('konachan', 'Konachan');
|
||||
}
|
||||
|
||||
public function getInfo(): BooruSourceInfo {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function getPosts(string $tags, bool $random = true, int $limit = 20): array {
|
||||
if($random)
|
||||
$tags .= ' order:random';
|
||||
|
||||
$params = [];
|
||||
if($tags !== '')
|
||||
$params['tags'] = $tags;
|
||||
if($limit > 0)
|
||||
$params['limit'] = $limit;
|
||||
|
||||
$url = self::POSTS_URL;
|
||||
if(!empty($params))
|
||||
$url .= '&' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_CERTINFO => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 5,
|
||||
CURLOPT_PATH_AS_IS => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => sprintf('Satori/%s (+https://fii.moe/satori)', SAT_VERSION),
|
||||
CURLOPT_HTTPHEADER => ['Accept: application/json'],
|
||||
]);
|
||||
$response = json_decode(curl_exec($curl), true);
|
||||
curl_close($curl);
|
||||
|
||||
return empty($response) || empty($response['posts']) || !is_array($response['posts'])
|
||||
? [] : XArray::select($response['posts'], fn($post) => new KonachanPost($post));
|
||||
}
|
||||
}
|
34
src/Booru/Yandere/YanderePost.php
Normal file
34
src/Booru/Yandere/YanderePost.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Yandere;
|
||||
|
||||
use Satori\Booru\IBooruPost;
|
||||
|
||||
class YanderePost implements IBooruPost {
|
||||
public function __construct(private array $info) {}
|
||||
|
||||
public function getId(): string {
|
||||
return (string)$this->info['id'];
|
||||
}
|
||||
|
||||
public function getRating(): string {
|
||||
return $this->info['rating'];
|
||||
}
|
||||
|
||||
public function getPostUrl(): string {
|
||||
return sprintf('https://yande.re/post/show/%d', $this->info['id']);
|
||||
}
|
||||
|
||||
public function getTags(): array {
|
||||
return explode(' ', $this->info['tags']);
|
||||
}
|
||||
|
||||
public function hasFileUrl(): bool {
|
||||
// yandere images get fucking massive sometimes
|
||||
// and make the upload process timeout and silently fail
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFileUrl(): string {
|
||||
return $this->info['sample_url'];
|
||||
}
|
||||
}
|
78
src/Booru/Yandere/YandereSource.php
Normal file
78
src/Booru/Yandere/YandereSource.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
namespace Satori\Booru\Yandere;
|
||||
|
||||
use Index\XArray;
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\Booru\BooruSourceInfo;
|
||||
use Satori\Booru\IBooruSource;
|
||||
|
||||
class YandereSource implements IBooruSource {
|
||||
private const POSTS_URL = 'https://yande.re/post.json?api_version=2';
|
||||
|
||||
private BooruSourceInfo $info;
|
||||
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->info = new BooruSourceInfo('yandere', 'Yandere');
|
||||
}
|
||||
|
||||
public function getInfo(): BooruSourceInfo {
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function getPosts(string $tags, bool $random = true, int $limit = 20): array {
|
||||
$localRandom = false;
|
||||
$localLimit = $limit;
|
||||
|
||||
if($random) {
|
||||
$localRandom = trim($tags) !== '';
|
||||
|
||||
if($localRandom)
|
||||
$limit = max($limit, 200);
|
||||
else
|
||||
$tags = 'order:random';
|
||||
}
|
||||
|
||||
$params = [];
|
||||
if($tags !== '')
|
||||
$params['tags'] = $tags;
|
||||
if($limit > 0)
|
||||
$params['limit'] = $limit;
|
||||
|
||||
$url = self::POSTS_URL;
|
||||
if(!empty($params))
|
||||
$url .= '&' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
$curl = curl_init($url);
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_CERTINFO => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 5,
|
||||
CURLOPT_PATH_AS_IS => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_USERAGENT => sprintf('Satori/%s (+https://fii.moe/satori)', SAT_VERSION),
|
||||
CURLOPT_HTTPHEADER => ['Accept: application/json'],
|
||||
]);
|
||||
$response = json_decode(curl_exec($curl), true);
|
||||
curl_close($curl);
|
||||
|
||||
if(empty($response) || empty($response['posts']) || !is_array($response['posts']))
|
||||
return [];
|
||||
|
||||
$posts = $response['posts'];
|
||||
|
||||
if($localRandom) {
|
||||
shuffle($posts);
|
||||
if($localLimit > 0)
|
||||
$posts = array_slice($posts, 0, $localLimit);
|
||||
}
|
||||
|
||||
return XArray::select($posts, fn($post) => new YanderePost($post));
|
||||
}
|
||||
}
|
35
src/Dictionary/DictionaryContext.php
Normal file
35
src/Dictionary/DictionaryContext.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
namespace Satori\Dictionary;
|
||||
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\SHttp;
|
||||
|
||||
class DictionaryContext {
|
||||
private IConfig $config;
|
||||
|
||||
public function __construct(IConfig $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function defineWord(string $word): array {
|
||||
$data = SHttp::getJson(sprintf(
|
||||
'https://api.wordnik.com/v4/word.json/%s/definitions?limit=5&includeRelated=false&sourceDictionaries=ahd-5%%2Cwordnet&useCanonical=true&includeTags=false&api_key=%s',
|
||||
rawurlencode($word),
|
||||
$this->config->getString('token')
|
||||
));
|
||||
|
||||
if(empty($data) || !empty($data['statusCode']))
|
||||
return [];
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach($data as $result)
|
||||
$results[] = new DictionaryResult($result);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
42
src/Dictionary/DictionaryResult.php
Normal file
42
src/Dictionary/DictionaryResult.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
namespace Satori\Dictionary;
|
||||
|
||||
class DictionaryResult {
|
||||
public function __construct(private array $info) {}
|
||||
|
||||
public function getWord(): string {
|
||||
return $this->info['word'] ?? '';
|
||||
}
|
||||
|
||||
public function getPartOfSpeech(): string {
|
||||
return $this->info['partOfSpeech'] ?? '';
|
||||
}
|
||||
|
||||
public function getAttributionText(): string {
|
||||
return $this->info['attributionText'] ?? '';
|
||||
}
|
||||
|
||||
public function getAttributionUrl(): string {
|
||||
return $this->info['attributionUrl'] ?? '';
|
||||
}
|
||||
|
||||
public function getSourceDictionary(): string {
|
||||
return $this->info['sourceDictionary'] ?? '';
|
||||
}
|
||||
|
||||
public function getWordnikUrl(): string {
|
||||
return $this->info['wordnikUrl'] ?? '';
|
||||
}
|
||||
|
||||
public function hasText(): bool {
|
||||
return array_key_exists('text', $this->info)
|
||||
&& $this->info['text'] !== '';
|
||||
}
|
||||
|
||||
public function getText(): string {
|
||||
return strtr($this->info['text'] ?? '', [
|
||||
'<em>' => '[i]',
|
||||
'</em>' => '[/i]',
|
||||
]);
|
||||
}
|
||||
}
|
64
src/Dictionary/DictionaryRoutes.php
Normal file
64
src/Dictionary/DictionaryRoutes.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
namespace Satori\Dictionary;
|
||||
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
||||
|
||||
class DictionaryRoutes extends RouteHandler {
|
||||
public function __construct(
|
||||
private DictionaryContext $context
|
||||
) {}
|
||||
|
||||
#[HttpGet('/dictionary/define')]
|
||||
public function getDefine($response, $request) {
|
||||
$word = trim((string)$request->getParam('word'));
|
||||
if($word === '')
|
||||
return ['error' => 'word'];
|
||||
|
||||
$results = $this->context->defineWord($word);
|
||||
$response = [];
|
||||
|
||||
foreach($results as $result) {
|
||||
if(!$result->hasText())
|
||||
continue;
|
||||
|
||||
$response[] = [
|
||||
'word' => $result->getWord(),
|
||||
'part_of_speech' => $result->getPartOfSpeech(),
|
||||
'attribution_text' => $result->getAttributionText(),
|
||||
'attribution_url' => $result->getAttributionUrl(),
|
||||
'source_dictionary' => $result->getSourceDictionary(),
|
||||
'text' => $result->getText(),
|
||||
'wordnik_url' => $result->getWordnikUrl(),
|
||||
];
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
#[HttpGet('/word-define.php')]
|
||||
public function getPHP($response, $request) {
|
||||
$word = trim((string)$request->getParam('word'));
|
||||
if($word === '')
|
||||
return ['error' => 'word'];
|
||||
|
||||
$results = $this->context->defineWord($word);
|
||||
$response = [];
|
||||
|
||||
foreach($results as $result) {
|
||||
if(!$result->hasText())
|
||||
continue;
|
||||
|
||||
$response[] = [
|
||||
'word' => $result->getWord(),
|
||||
'pos' => $result->getPartOfSpeech(),
|
||||
'attr' => $result->getAttributionText(),
|
||||
'attr_url' => $result->getAttributionUrl(),
|
||||
'dict' => $result->getSourceDictionary(),
|
||||
'text' => $result->getText(),
|
||||
'url' => $result->getWordnikUrl(),
|
||||
];
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
69
src/ExRate/ExRateContext.php
Normal file
69
src/ExRate/ExRateContext.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
namespace Satori\ExRate;
|
||||
|
||||
use Index\Data\IDbConnection;
|
||||
use Syokuhou\IConfig;
|
||||
use Satori\SHttp;
|
||||
|
||||
class ExRateContext {
|
||||
private const BASE_CURRENCY = 'EUR';
|
||||
private const COMMON_CURRENCIES = [
|
||||
'EUR', 'AUD', 'GBP', 'CAD', 'USD',
|
||||
'JPY', 'PLN', 'SGD', 'RUB', 'ILS',
|
||||
];
|
||||
|
||||
private IConfig $config;
|
||||
private ExRateDatabase $database;
|
||||
|
||||
public function __construct(IConfig $config, IDbConnection $dbConn) {
|
||||
$this->config = $config;
|
||||
$this->database = new ExRateDatabase(self::BASE_CURRENCY, $dbConn);
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getDatabase(): ExRateDatabase {
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
public function getBaseCurrency(): string {
|
||||
return self::BASE_CURRENCY;
|
||||
}
|
||||
|
||||
public function getCommonCurrencies(): array {
|
||||
return self::COMMON_CURRENCIES;
|
||||
}
|
||||
|
||||
public function ratesNeedRefresh(): bool {
|
||||
return $this->database->ratesNeedRefresh();
|
||||
}
|
||||
|
||||
public function refreshRates(): void {
|
||||
// HTTPS is a paid feature?????????
|
||||
$data = SHttp::getJsonCached(sprintf(
|
||||
'http://api.exchangerate.host/live?source=%s&access_key=%s',
|
||||
self::BASE_CURRENCY,
|
||||
$this->config->getString('token')
|
||||
));
|
||||
|
||||
if(empty($data) || !$data['success'])
|
||||
return;
|
||||
|
||||
$this->database->clearRates();
|
||||
$this->database->insertRate(self::BASE_CURRENCY, 1.0);
|
||||
|
||||
foreach($data['quotes'] as $names => $value) {
|
||||
$to = substr($names, 3, 3);
|
||||
if(strlen($to) !== 3)
|
||||
continue;
|
||||
|
||||
$this->database->insertRate($to, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function convertRate(string $from, string $to, float $value): float {
|
||||
return $this->database->convertRate($from, $to, $value);
|
||||
}
|
||||
}
|
49
src/ExRate/ExRateDatabase.php
Normal file
49
src/ExRate/ExRateDatabase.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
namespace Satori\ExRate;
|
||||
|
||||
use Index\Data\DbStatementCache;
|
||||
use Index\Data\IDbConnection;
|
||||
|
||||
class ExRateDatabase {
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(
|
||||
private string $baseCurrency,
|
||||
private IDbConnection $dbConn
|
||||
) {
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public function ratesNeedRefresh(): bool {
|
||||
$result = $this->dbConn->query('SELECT MAX(rate_stored) > NOW() - INTERVAL 1 DAY FROM `exchange-rates` LIMIT 1');
|
||||
return !$result->next() || $result->isNull(0) || $result->getInteger(0) < 1;
|
||||
}
|
||||
|
||||
public function clearRates(): void {
|
||||
$this->dbConn->execute('TRUNCATE `exchange-rates`');
|
||||
}
|
||||
|
||||
public function insertRate(string $to, float $rate): void {
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'INSERT INTO `exchange-rates` (rate_from, rate_to, rate_value) VALUES ("%s", ?, ?)',
|
||||
$this->baseCurrency
|
||||
));
|
||||
$stmt->addParameter(1, $to);
|
||||
$stmt->addParameter(2, $rate);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function convertRate(string $from, string $to, float $value): float {
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'SELECT (SELECT (? / rate_value) FROM `exchange-rates` WHERE rate_from = "%1$s" AND rate_to = ?) * rate_value FROM `exchange-rates` WHERE rate_from = "%1$s" AND rate_to = ?',
|
||||
$this->baseCurrency
|
||||
));
|
||||
$stmt->addParameter(1, $value);
|
||||
$stmt->addParameter(2, $from);
|
||||
$stmt->addParameter(3, $to);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? $result->getFloat(0) : 0;
|
||||
}
|
||||
}
|
92
src/ExRate/ExRateRoutes.php
Normal file
92
src/ExRate/ExRateRoutes.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
namespace Satori\ExRate;
|
||||
|
||||
use stdClass;
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
||||
|
||||
class ExRateRoutes extends RouteHandler {
|
||||
public function __construct(
|
||||
private ExRateContext $context
|
||||
) {}
|
||||
|
||||
#[HttpGet('/exrate/convert')]
|
||||
public function getConvert($response, $request) {
|
||||
$from = strtoupper((string)$request->getParam('from'));
|
||||
$targets = explode(',', strtoupper((string)$request->getParam('to')));
|
||||
$amount = (string)($request->getParam('amount', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) ?? '1');
|
||||
|
||||
if(strlen($from) !== 3)
|
||||
return 400;
|
||||
|
||||
if(empty($targets) || empty($targets[0]))
|
||||
$targets = $this->context->getCommonCurrencies();
|
||||
else
|
||||
foreach($targets as $target)
|
||||
if(strlen($target) !== 3)
|
||||
return 400;
|
||||
|
||||
if($this->context->ratesNeedRefresh())
|
||||
$this->context->refreshRates();
|
||||
|
||||
$response = new stdClass;
|
||||
$response->from = $from;
|
||||
$response->amount = (float)$amount;
|
||||
$response->results = [];
|
||||
|
||||
foreach($targets as $target) {
|
||||
if($target === $from)
|
||||
continue;
|
||||
|
||||
$response->results[] = [
|
||||
'to' => $target,
|
||||
'result' => $this->context->convertRate($from, $target, $amount),
|
||||
];
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
#[HttpGet('/exrate.php')]
|
||||
public function getPHP($response, $request) {
|
||||
$from = strtoupper((string)$request->getParam('from'));
|
||||
$to = strtoupper((string)$request->getParam('to'));
|
||||
$amount = (string)($request->getParam('amount', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) ?? '1');
|
||||
|
||||
if((!empty($to) && strlen($to) !== 3) || strlen($from) !== 3) {
|
||||
$response->setStatusCode(404);
|
||||
return 'Invalid currency specified.';
|
||||
}
|
||||
|
||||
if($this->context->ratesNeedRefresh())
|
||||
$this->context->refreshRates();
|
||||
|
||||
|
||||
$response = new stdClass;
|
||||
$response->from = $from;
|
||||
$response->to = $to;
|
||||
$response->amount = (float)$amount;
|
||||
|
||||
if($from === $to) {
|
||||
$response->result = $response->amount;
|
||||
return $response;
|
||||
}
|
||||
|
||||
$singleResult = !empty($to);
|
||||
$targets = $singleResult ? [$to] : $this->context->getCommonCurrencies();
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach($targets as $to)
|
||||
$results[] = [
|
||||
'to' => $to,
|
||||
'result' => $this->context->convertRate($from, $to, $amount),
|
||||
];
|
||||
|
||||
if($singleResult)
|
||||
$response->result = $results[0]['result'];
|
||||
else
|
||||
$response->results = $results;
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,65 @@
|
|||
<?php
|
||||
namespace Satori;
|
||||
|
||||
use Index\Data\IDbConnection;
|
||||
use Syokuhou\IConfig;
|
||||
|
||||
final class SatoriContext {
|
||||
private IConfig $config;
|
||||
private DatabaseContext $dbCtx;
|
||||
|
||||
public function __construct(IConfig $config, IDbConnection $dbConn) {
|
||||
$this->config = $config;
|
||||
$this->dbCtx = new DatabaseContext($dbConn);
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getDatabase(): DatabaseContext {
|
||||
return $this->dbCtx;
|
||||
}
|
||||
|
||||
public function createExRates(): ExRate\ExRateContext {
|
||||
return new ExRate\ExRateContext(
|
||||
$this->config->scopeTo('exrate'),
|
||||
$this->dbCtx->getConnection()
|
||||
);
|
||||
}
|
||||
|
||||
public function createDictionary(): Dictionary\DictionaryContext {
|
||||
return new Dictionary\DictionaryContext(
|
||||
$this->config->scopeTo('dictionary')
|
||||
);
|
||||
}
|
||||
|
||||
public function createTranslation(): Translation\TranslateContext {
|
||||
return new Translation\TranslateContext(
|
||||
$this->config->scopeTo('translate')
|
||||
);
|
||||
}
|
||||
|
||||
public function createBooru(): Booru\BooruContext {
|
||||
return new Booru\BooruContext(
|
||||
$this->config->scopeTo('booru')
|
||||
);
|
||||
}
|
||||
|
||||
public function createSplatoon(): Splatoon\SplatoonContext {
|
||||
return new Splatoon\SplatoonContext(
|
||||
$this->config->scopeTo('splatoon')
|
||||
);
|
||||
}
|
||||
|
||||
public function createRouting(): RoutingContext {
|
||||
$routingCtx = new RoutingContext;
|
||||
$routingCtx->register(new Splatoon\SplatoonRoutes(new Splatoon\SplatoonContext));
|
||||
|
||||
$routingCtx->register(new Booru\BooruRoutes($this->createBooru()));
|
||||
$routingCtx->register(new Dictionary\DictionaryRoutes($this->createDictionary()));
|
||||
$routingCtx->register(new ExRate\ExRateRoutes($this->createExRates()));
|
||||
$routingCtx->register(new Splatoon\SplatoonRoutes($this->createSplatoon()));
|
||||
$routingCtx->register(new Translation\TranslateRoutes($this->createTranslation()));
|
||||
|
||||
return $routingCtx;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<?php
|
||||
namespace Satori\Splatoon;
|
||||
|
||||
use Syokuhou\IConfig;
|
||||
|
||||
class SplatoonContext {
|
||||
private array $games = [];
|
||||
|
||||
public function __construct() {
|
||||
public function __construct(private IConfig $config) {
|
||||
$this->games[] = new Splatoon3\Splatoon3Game;
|
||||
$this->games[] = new Splatoon2\Splatoon2Game;
|
||||
$this->games[] = new Splatoon1\Splatoon1Game;
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getGames(): array {
|
||||
return $this->games;
|
||||
}
|
||||
|
|
85
src/Translation/TranslateContext.php
Normal file
85
src/Translation/TranslateContext.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
namespace Satori\Translation;
|
||||
|
||||
use stdClass;
|
||||
use Syokuhou\IConfig;
|
||||
|
||||
class TranslateContext {
|
||||
public const GOOGLE_TRANSLATE = 'https://translate.google.com/translate_a/single?ie=UTF-8&oe=UTF-8&multires=1&client=gtx&sl=%s&tl=%s&dt=t&q=%s';
|
||||
public const GOOGLE_USERAGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36';
|
||||
public const GOOGLE_LANGS = [
|
||||
'af', 'sq', 'ar', 'hy', 'az', 'eu', 'be', 'bn', 'bs',
|
||||
'bg', 'ca', 'ny', 'co', 'hr', 'cs', 'da', 'nl', 'en',
|
||||
'eo', 'tl', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el',
|
||||
'gu', 'ht', 'ha', 'iw', 'hi', 'hu', 'is', 'ig', 'id',
|
||||
'ga', 'ja', 'jw', 'kn', 'kk', 'km', 'ko', 'ku', 'ky',
|
||||
'lo', 'la', 'lv', 'lt', 'lb', 'mk', 'mg', 'ms', 'ml',
|
||||
'mt', 'mi', 'mr', 'mn', 'my', 'ne', 'no', 'ps', 'fa',
|
||||
'pl', 'pt', 'ma', 'ro', 'ru', 'sm', 'gd', 'sr', 'st',
|
||||
'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', 'su', 'sw',
|
||||
'sv', 'tg', 'ta', 'te', 'th', 'tr', 'uk', 'ur', 'uz',
|
||||
'vi', 'cy', 'xh', 'yi', 'yo', 'zu', 'it',
|
||||
'ceb', 'haw', 'hmn',
|
||||
'zh-cn', 'zh-tw',
|
||||
];
|
||||
|
||||
private IConfig $config;
|
||||
|
||||
public function __construct(IConfig $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getConfig(): IConfig {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function getGoogleLanguageCodes(): array {
|
||||
return self::GOOGLE_LANGS;
|
||||
}
|
||||
|
||||
public function getGoogleLanguages(): array {
|
||||
$langs = [];
|
||||
|
||||
foreach(self::GOOGLE_LANGS as $langId)
|
||||
$langs[] = [
|
||||
'code' => $langId,
|
||||
'name' => locale_get_display_name($langId),
|
||||
];
|
||||
|
||||
return $langs;
|
||||
}
|
||||
|
||||
public function googleTranslate(string $from, string $to, string $text): ?object {
|
||||
$curl = curl_init(sprintf(
|
||||
self::GOOGLE_TRANSLATE,
|
||||
rawurlencode($from),
|
||||
rawurlencode($to),
|
||||
rawurlencode($text)
|
||||
));
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_USERAGENT => self::GOOGLE_USERAGENT,
|
||||
]);
|
||||
$response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
if(empty($response))
|
||||
return null;
|
||||
|
||||
$response = json_decode($response);
|
||||
if(empty($response))
|
||||
return null;
|
||||
|
||||
$result = new stdClass;
|
||||
$result->translatedText = $response[0][0][0] ?? '';
|
||||
$result->originalText = $response[0][0][1] ?? '';
|
||||
$result->fromLangCode = $response[2] ?? '';
|
||||
$result->fromLangName = locale_get_display_name($result->fromLangCode);
|
||||
$result->toLangCode = $to;
|
||||
$result->toLangName = locale_get_display_name($to);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
98
src/Translation/TranslateRoutes.php
Normal file
98
src/Translation/TranslateRoutes.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
namespace Satori\Translation;
|
||||
|
||||
use DOMDocument;
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
||||
|
||||
class TranslateRoutes extends RouteHandler {
|
||||
public function __construct(
|
||||
private TranslateContext $context
|
||||
) {}
|
||||
|
||||
#[HttpGet('/translate.php')]
|
||||
public function getPHP($response, $request) {
|
||||
$format = (string)($request->getParam('fmt') ?? 'json');
|
||||
if($format !== 'json' && $format !== 'xml')
|
||||
return '';
|
||||
|
||||
$mode = (string)$request->getParam('m');
|
||||
|
||||
if($mode === 'list') {
|
||||
$langs = $this->context->getGoogleLanguages();
|
||||
|
||||
if($format === 'xml') {
|
||||
$document = new DOMDocument('1.0', 'UTF-8');
|
||||
|
||||
$array = $document->createElement('ArrayOfLanguage');
|
||||
$document->appendChild($array);
|
||||
|
||||
foreach($langs as $langInfo) {
|
||||
$lang = $document->createElement('Language');
|
||||
|
||||
foreach($langInfo as $name => $value)
|
||||
$lang->setAttribute($name, $value);
|
||||
|
||||
$array->appendChild($lang);
|
||||
}
|
||||
|
||||
return $document->saveXML();
|
||||
}
|
||||
|
||||
return $langs;
|
||||
}
|
||||
|
||||
if($mode === 'do') {
|
||||
$from = (string)($request->getParam('f') ?? 'auto');
|
||||
$to = (string)($request->getParam('t') ?? 'en');
|
||||
$langs = $this->context->getGoogleLanguageCodes();
|
||||
|
||||
$encodeOutput = function(array $info) use ($format) {
|
||||
if($format === 'json')
|
||||
return $info;
|
||||
|
||||
if($format === 'xml') {
|
||||
$document = new DOMDocument('1.0', 'UTF-8');
|
||||
$translation = $document->createElement('Translation');
|
||||
|
||||
foreach($info as $name => $value)
|
||||
$translation->setAttribute($name, $value);
|
||||
|
||||
$document->appendChild($translation);
|
||||
|
||||
return $document->saveXML();
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
if($from !== 'auto' && !in_array($from, $langs))
|
||||
return $encodeOutput(['error' => 'Source language is not supported.']);
|
||||
if(!in_array($to, $langs))
|
||||
return $encodeOutput(['error' => 'Target language is not supported.']);
|
||||
|
||||
$text = '';
|
||||
if($request->hasParam('z'))
|
||||
$text = trim((string)$request->getParam('z'));
|
||||
elseif($request->hasContent())
|
||||
$text = trim((string)$request->getContent());
|
||||
|
||||
if($text === '')
|
||||
return $encodeOutput(['error' => 'Nothing to translate.']);
|
||||
|
||||
$result = $this->context->googleTranslate($from, $to, $text);
|
||||
if($result === null)
|
||||
return $encodeOutput(['error' => 'Translation failed.']);
|
||||
|
||||
return $encodeOutput([
|
||||
'from' => $result->fromLangCode,
|
||||
'from_text' => $result->fromLangName,
|
||||
'to' => $result->toLangCode,
|
||||
'to_text' => $result->toLangName,
|
||||
'original' => $result->originalText,
|
||||
'result' => $result->translatedText,
|
||||
]);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
Reference in a new issue