Primer commit del sistema separado falta mejorar mucho
This commit is contained in:
9
vendor/discord-php/http/.gitignore
vendored
Executable file
9
vendor/discord-php/http/.gitignore
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
/vendor/
|
||||
composer.lock
|
||||
test.php
|
||||
.php_cs.cache
|
||||
.php_cs
|
||||
.php-cs-fixer.php
|
||||
.php-cs-fixer.cache
|
||||
.vscode
|
||||
.phpunit.cache
|
||||
102
vendor/discord-php/http/.php-cs-fixer.dist.php
vendored
Executable file
102
vendor/discord-php/http/.php-cs-fixer.dist.php
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
$header = <<<'EOF'
|
||||
This file is a part of the DiscordPHP-Http project.
|
||||
|
||||
Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
|
||||
This file is subject to the MIT license that is bundled
|
||||
with this source code in the LICENSE file.
|
||||
EOF;
|
||||
|
||||
$fixers = [
|
||||
'blank_line_after_namespace',
|
||||
'braces',
|
||||
'class_definition',
|
||||
'elseif',
|
||||
'encoding',
|
||||
'full_opening_tag',
|
||||
'function_declaration',
|
||||
'lowercase_keywords',
|
||||
'method_argument_space',
|
||||
'no_closing_tag',
|
||||
'no_spaces_after_function_name',
|
||||
'no_spaces_inside_parenthesis',
|
||||
'no_trailing_whitespace',
|
||||
'no_trailing_whitespace_in_comment',
|
||||
'single_blank_line_at_eof',
|
||||
'single_class_element_per_statement',
|
||||
'single_import_per_statement',
|
||||
'single_line_after_imports',
|
||||
'switch_case_semicolon_to_colon',
|
||||
'switch_case_space',
|
||||
'visibility_required',
|
||||
'blank_line_after_opening_tag',
|
||||
'no_multiline_whitespace_around_double_arrow',
|
||||
'no_empty_statement',
|
||||
'include',
|
||||
'no_trailing_comma_in_list_call',
|
||||
'not_operator_with_successor_space',
|
||||
'no_leading_namespace_whitespace',
|
||||
'no_blank_lines_after_class_opening',
|
||||
'no_blank_lines_after_phpdoc',
|
||||
'object_operator_without_whitespace',
|
||||
'binary_operator_spaces',
|
||||
'phpdoc_indent',
|
||||
'general_phpdoc_tag_rename',
|
||||
'phpdoc_inline_tag_normalizer',
|
||||
'phpdoc_tag_type',
|
||||
'phpdoc_no_access',
|
||||
'phpdoc_no_package',
|
||||
'phpdoc_scalar',
|
||||
'phpdoc_summary',
|
||||
'phpdoc_to_comment',
|
||||
'phpdoc_trim',
|
||||
'phpdoc_var_without_name',
|
||||
'no_leading_import_slash',
|
||||
'no_trailing_comma_in_singleline_array',
|
||||
'single_blank_line_before_namespace',
|
||||
'single_quote',
|
||||
'no_singleline_whitespace_before_semicolons',
|
||||
'cast_spaces',
|
||||
'standardize_not_equals',
|
||||
'ternary_operator_spaces',
|
||||
'trim_array_spaces',
|
||||
'unary_operator_spaces',
|
||||
'no_unused_imports',
|
||||
'no_useless_else',
|
||||
'no_useless_return',
|
||||
'phpdoc_no_empty_return',
|
||||
'no_extra_blank_lines',
|
||||
'multiline_whitespace_before_semicolons',
|
||||
];
|
||||
|
||||
$rules = [
|
||||
'concat_space' => ['spacing' => 'none'],
|
||||
'phpdoc_no_alias_tag' => ['replacements' => ['type' => 'var']],
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'binary_operator_spaces' => ['align_double_arrow' => true, 'align_equals' => true],
|
||||
'header_comment' => ['header' => $header],
|
||||
'indentation_type' => true,
|
||||
'phpdoc_align' => [
|
||||
'align' => 'vertical',
|
||||
'tags' => ['param', 'property', 'property-read', 'property-write', 'return', 'throws', 'type', 'var', 'method'],
|
||||
],
|
||||
'blank_line_before_statement' => ['statements' => ['return']],
|
||||
'constant_case' => ['case' => 'lower'],
|
||||
'echo_tag_syntax' => ['format' => 'long'],
|
||||
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
|
||||
];
|
||||
|
||||
foreach ($fixers as $fix) {
|
||||
$rules[$fix] = true;
|
||||
}
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
|
||||
return $config
|
||||
->setRules($rules)
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__)
|
||||
);
|
||||
22
vendor/discord-php/http/LICENSE
vendored
Executable file
22
vendor/discord-php/http/LICENSE
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021-present David Cole <david.cole1340@gmail.com> and all
|
||||
contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
92
vendor/discord-php/http/README.md
vendored
Executable file
92
vendor/discord-php/http/README.md
vendored
Executable file
@@ -0,0 +1,92 @@
|
||||
# DiscordPHP-Http
|
||||
|
||||
Asynchronous HTTP client used for communication with the Discord REST API.
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP >=7.4
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
$ composer require discord-php/http
|
||||
```
|
||||
|
||||
A [psr/log](https://packagist.org/packages/psr/log)-compliant logging library is also required. We recommend [monolog](https://github.com/Seldaek/monolog) which will be used in examples.
|
||||
|
||||
## Usage
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
include 'vendor/autoload.php';
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Discord\Http\Http;
|
||||
use Discord\Http\Drivers\React;
|
||||
|
||||
$loop = \React\EventLoop\Factory::create();
|
||||
$logger = (new Logger('logger-name'))->pushHandler(new StreamHandler('php://output'));
|
||||
$http = new Http(
|
||||
'Bot xxxx.yyyy.zzzz',
|
||||
$loop,
|
||||
$logger
|
||||
);
|
||||
|
||||
// set up a driver - this example uses the React driver
|
||||
$driver = new React($loop);
|
||||
$http->setDriver($driver);
|
||||
|
||||
// must be the last line
|
||||
$loop->run();
|
||||
```
|
||||
|
||||
All request methods have the same footprint:
|
||||
|
||||
```php
|
||||
$http->get(string $url, $content = null, array $headers = []);
|
||||
$http->post(string $url, $content = null, array $headers = []);
|
||||
$http->put(string $url, $content = null, array $headers = []);
|
||||
$http->patch(string $url, $content = null, array $headers = []);
|
||||
$http->delete(string $url, $content = null, array $headers = []);
|
||||
```
|
||||
|
||||
For other methods:
|
||||
|
||||
```php
|
||||
$http->queueRequest(string $method, string $url, $content, array $headers = []);
|
||||
```
|
||||
|
||||
All methods return the decoded JSON response in an object:
|
||||
|
||||
```php
|
||||
// https://discord.com/api/v8/oauth2/applications/@me
|
||||
$http->get('oauth2/applications/@me')->done(function ($response) {
|
||||
var_dump($response);
|
||||
}, function ($e) {
|
||||
echo "Error: ".$e->getMessage().PHP_EOL;
|
||||
});
|
||||
```
|
||||
|
||||
Most Discord endpoints are provided in the [Endpoint.php](src/Discord/Endpoint.php) class as constants. Parameters start with a colon,
|
||||
e.g. `channels/:channel_id/messages/:message_id`. You can bind parameters to then with the same class:
|
||||
|
||||
```php
|
||||
// channels/channel_id_here/messages/message_id_here
|
||||
$endpoint = Endpoint::bind(Endpoint::CHANNEL_MESSAGE, 'channel_id_here', 'message_id_here');
|
||||
|
||||
$http->get($endpoint)->done(...);
|
||||
```
|
||||
|
||||
It is recommended that if the endpoint contains parameters you use the `Endpoint::bind()` function to sort requests into their correct rate limit buckets.
|
||||
For an example, see [DiscordPHP](https://github.com/discord-php/DiscordPHP).
|
||||
|
||||
## License
|
||||
|
||||
This software is licensed under the MIT license which can be viewed in the [LICENSE](LICENSE) file.
|
||||
|
||||
## Credits
|
||||
|
||||
- [David Cole](mailto:david.cole1340@gmail.com)
|
||||
- All contributors
|
||||
43
vendor/discord-php/http/composer.json
vendored
Executable file
43
vendor/discord-php/http/composer.json
vendored
Executable file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "discord-php/http",
|
||||
"description": "Handles HTTP requests to Discord servers",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Cole",
|
||||
"email": "david.cole1340@gmail.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Discord\\Http\\": "src/Discord",
|
||||
"Tests\\Discord\\Http\\": "tests/Discord"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0",
|
||||
"react/http": "^1.2",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"react/promise": "^2.2 || ^3.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"guzzlehttp/guzzle": "For alternative to ReactPHP/Http Browser"
|
||||
},
|
||||
"require-dev": {
|
||||
"monolog/monolog": "^2.2",
|
||||
"friendsofphp/php-cs-fixer": "^2.17",
|
||||
"psy/psysh": "^0.10.6",
|
||||
"guzzlehttp/guzzle": "^6.0|^7.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"mockery/mockery": "^1.5",
|
||||
"react/async": "^4 || ^3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "php tests/Drivers/_server.php& HTTP_SERVER_PID=$!; ./vendor/bin/phpunit; kill $HTTP_SERVER_PID;",
|
||||
"test-discord": "./vendor/bin/phpunit --testsuite Discord",
|
||||
"test-drivers": "php tests/Drivers/_server.php& HTTP_SERVER_PID=$!; ./vendor/bin/phpunit --testsuite Drivers; kill $HTTP_SERVER_PID;",
|
||||
"test-coverage": "php tests/Drivers/_server.php& HTTP_SERVER_PID=$!; php -d xdebug.mode=coverage ./vendor/bin/phpunit --coverage-text; kill $HTTP_SERVER_PID;",
|
||||
"test-coverage-html": "php tests/Drivers/_server.php& HTTP_SERVER_PID=$!; php -d xdebug.mode=coverage ./vendor/bin/phpunit --coverage-html .phpunit.cache/cov-html; kill $HTTP_SERVER_PID;"
|
||||
}
|
||||
}
|
||||
66
vendor/discord-php/http/examples/file-upload.php
vendored
Executable file
66
vendor/discord-php/http/examples/file-upload.php
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
use Discord\Http\Drivers\Guzzle;
|
||||
use Discord\Http\Endpoint;
|
||||
use Discord\Http\Http;
|
||||
use Discord\Http\Multipart\MultipartBody;
|
||||
use Discord\Http\Multipart\MultipartField;
|
||||
use Psr\Log\NullLogger;
|
||||
use React\EventLoop\Loop;
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
$http = new Http(
|
||||
'Your token',
|
||||
Loop::get(),
|
||||
new NullLogger(),
|
||||
new Guzzle(
|
||||
Loop::get()
|
||||
)
|
||||
);
|
||||
|
||||
$jsonPayloadField = new MultipartField(
|
||||
'json_payload',
|
||||
json_encode([
|
||||
'content' => 'Hello!',
|
||||
]),
|
||||
['Content-Type' => 'application/json']
|
||||
);
|
||||
|
||||
$imageField = new MultipartField(
|
||||
'files[0]',
|
||||
file_get_contents('/path/to/image.png'),
|
||||
['Content-Type' => 'image/png'],
|
||||
'image.png'
|
||||
);
|
||||
|
||||
$multipart = new MultipartBody([
|
||||
$jsonPayloadField,
|
||||
$imageField,
|
||||
]);
|
||||
|
||||
$http->post(
|
||||
Endpoint::bind(
|
||||
Endpoint::CHANNEL_MESSAGES,
|
||||
'Channel ID'
|
||||
),
|
||||
$multipart
|
||||
)->then(
|
||||
function ($response) {
|
||||
// Do something with response..
|
||||
},
|
||||
function (Exception $e) {
|
||||
echo $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
);
|
||||
|
||||
Loop::run();
|
||||
31
vendor/discord-php/http/phpunit.xml
vendored
Executable file
31
vendor/discord-php/http/phpunit.xml
vendored
Executable file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
cacheResultFile=".phpunit.cache/test-results"
|
||||
executionOrder="depends,defects"
|
||||
forceCoversAnnotation="false"
|
||||
beStrictAboutCoversAnnotation="false"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
verbose="true">
|
||||
<testsuites>
|
||||
<testsuite name="Discord">
|
||||
<directory>tests/Discord</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Drivers">
|
||||
<directory>tests/Drivers</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<coverage cacheDirectory=".phpunit.cache/code-coverage"
|
||||
processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
226
vendor/discord-php/http/src/Discord/Bucket.php
vendored
Executable file
226
vendor/discord-php/http/src/Discord/Bucket.php
vendored
Executable file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\EventLoop\TimerInterface;
|
||||
use SplQueue;
|
||||
|
||||
/**
|
||||
* Represents a rate-limit bucket.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class Bucket
|
||||
{
|
||||
/**
|
||||
* Request queue.
|
||||
*
|
||||
* @var SplQueue
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* Bucket name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* ReactPHP event loop.
|
||||
*
|
||||
* @var LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
/**
|
||||
* HTTP logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Callback for when a request is ready.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $runRequest;
|
||||
|
||||
/**
|
||||
* Whether we are checking the queue.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $checkerRunning = false;
|
||||
|
||||
/**
|
||||
* Number of requests allowed before reset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $requestLimit;
|
||||
|
||||
/**
|
||||
* Number of remaining requests before reset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $requestRemaining;
|
||||
|
||||
/**
|
||||
* Timer to reset the bucket.
|
||||
*
|
||||
* @var TimerInterface
|
||||
*/
|
||||
protected $resetTimer;
|
||||
|
||||
/**
|
||||
* Whether react/promise v3 is used, if false, using v2.
|
||||
*/
|
||||
protected $promiseV3 = true;
|
||||
|
||||
/**
|
||||
* Bucket constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param callable $runRequest
|
||||
*/
|
||||
public function __construct(string $name, LoopInterface $loop, LoggerInterface $logger, callable $runRequest)
|
||||
{
|
||||
$this->queue = new SplQueue;
|
||||
$this->name = $name;
|
||||
$this->loop = $loop;
|
||||
$this->logger = $logger;
|
||||
$this->runRequest = $runRequest;
|
||||
|
||||
$this->promiseV3 = str_starts_with(InstalledVersions::getVersion('react/promise'), '3.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue a request.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function enqueue(Request $request)
|
||||
{
|
||||
$this->queue->enqueue($request);
|
||||
$this->logger->debug($this.' queued '.$request);
|
||||
$this->checkQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for requests in the bucket.
|
||||
*/
|
||||
public function checkQueue()
|
||||
{
|
||||
// We are already checking the queue.
|
||||
if ($this->checkerRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkerRunning = true;
|
||||
$this->__checkQueue();
|
||||
}
|
||||
|
||||
protected function __checkQueue()
|
||||
{
|
||||
// Check for rate-limits
|
||||
if ($this->requestRemaining < 1 && ! is_null($this->requestRemaining)) {
|
||||
$interval = 0;
|
||||
if ($this->resetTimer) {
|
||||
$interval = $this->resetTimer->getInterval() ?? 0;
|
||||
}
|
||||
$this->logger->info($this.' expecting rate limit, timer interval '.($interval * 1000).' ms');
|
||||
$this->checkerRunning = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue is empty, job done.
|
||||
if ($this->queue->isEmpty()) {
|
||||
$this->checkerRunning = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Request */
|
||||
$request = $this->queue->dequeue();
|
||||
|
||||
// Promises v3 changed `->then` to behave as `->done` and removed `->then`. We still need the behaviour of `->done` in projects using v2
|
||||
($this->runRequest)($request)->{$this->promiseV3 ? 'then' : 'done'}(function (ResponseInterface $response) {
|
||||
$resetAfter = (float) $response->getHeaderLine('X-Ratelimit-Reset-After');
|
||||
$limit = $response->getHeaderLine('X-Ratelimit-Limit');
|
||||
$remaining = $response->getHeaderLine('X-Ratelimit-Remaining');
|
||||
|
||||
if ($resetAfter) {
|
||||
$resetAfter = (float) $resetAfter;
|
||||
|
||||
if ($this->resetTimer) {
|
||||
$this->loop->cancelTimer($this->resetTimer);
|
||||
}
|
||||
|
||||
$this->resetTimer = $this->loop->addTimer($resetAfter, function () {
|
||||
// Reset requests remaining and check queue
|
||||
$this->requestRemaining = $this->requestLimit;
|
||||
$this->resetTimer = null;
|
||||
$this->checkQueue();
|
||||
});
|
||||
}
|
||||
|
||||
// Check if rate-limit headers are present and store
|
||||
if (is_numeric($limit)) {
|
||||
$this->requestLimit = (int) $limit;
|
||||
}
|
||||
|
||||
if (is_numeric($remaining)) {
|
||||
$this->requestRemaining = (int) $remaining;
|
||||
}
|
||||
|
||||
// Check for more requests
|
||||
$this->__checkQueue();
|
||||
}, function ($rateLimit) use ($request) {
|
||||
if ($rateLimit instanceof RateLimit) {
|
||||
$this->queue->enqueue($request);
|
||||
|
||||
// Bucket-specific rate-limit
|
||||
// Re-queue the request and wait the retry after time
|
||||
if (! $rateLimit->isGlobal()) {
|
||||
$this->loop->addTimer($rateLimit->getRetryAfter(), fn () => $this->__checkQueue());
|
||||
}
|
||||
// Stop the queue checker for a global rate-limit.
|
||||
// Will be restarted when global rate-limit finished.
|
||||
else {
|
||||
$this->checkerRunning = false;
|
||||
|
||||
$this->logger->debug($this.' stopping queue checker');
|
||||
}
|
||||
} else {
|
||||
$this->__checkQueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a bucket to a user-readable string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return 'BUCKET '.$this->name;
|
||||
}
|
||||
}
|
||||
34
vendor/discord-php/http/src/Discord/DriverInterface.php
vendored
Executable file
34
vendor/discord-php/http/src/Discord/DriverInterface.php
vendored
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Interface for an HTTP driver.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
interface DriverInterface
|
||||
{
|
||||
/**
|
||||
* Runs a request.
|
||||
*
|
||||
* Returns a promise resolved with a PSR response interface.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return PromiseInterface<ResponseInterface>
|
||||
*/
|
||||
public function runRequest(Request $request): PromiseInterface;
|
||||
}
|
||||
77
vendor/discord-php/http/src/Discord/Drivers/Guzzle.php
vendored
Executable file
77
vendor/discord-php/http/src/Discord/Drivers/Guzzle.php
vendored
Executable file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Drivers;
|
||||
|
||||
use Discord\Http\DriverInterface;
|
||||
use Discord\Http\Request;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* guzzlehttp/guzzle driver for Discord HTTP client. (still with React Promise).
|
||||
*
|
||||
* @author SQKo
|
||||
*/
|
||||
class Guzzle implements DriverInterface
|
||||
{
|
||||
/**
|
||||
* ReactPHP event loop.
|
||||
*
|
||||
* @var LoopInterface|null
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
/**
|
||||
* GuzzleHTTP/Guzzle client.
|
||||
*
|
||||
* @var Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Constructs the Guzzle driver.
|
||||
*
|
||||
* @param LoopInterface|null $loop
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(?LoopInterface $loop = null, array $options = [])
|
||||
{
|
||||
$this->loop = $loop;
|
||||
|
||||
// Allow 400 and 500 HTTP requests to be resolved rather than rejected.
|
||||
$options['http_errors'] = false;
|
||||
$this->client = new Client($options);
|
||||
}
|
||||
|
||||
public function runRequest(Request $request): PromiseInterface
|
||||
{
|
||||
// Create a React promise
|
||||
$deferred = new Deferred();
|
||||
$reactPromise = $deferred->promise();
|
||||
|
||||
$promise = $this->client->requestAsync($request->getMethod(), $request->getUrl(), [
|
||||
RequestOptions::HEADERS => $request->getHeaders(),
|
||||
RequestOptions::BODY => $request->getContent(),
|
||||
])->then([$deferred, 'resolve'], [$deferred, 'reject']);
|
||||
|
||||
if ($this->loop) {
|
||||
$this->loop->futureTick([$promise, 'wait']);
|
||||
} else {
|
||||
$promise->wait();
|
||||
}
|
||||
|
||||
return $reactPromise;
|
||||
}
|
||||
}
|
||||
72
vendor/discord-php/http/src/Discord/Drivers/React.php
vendored
Executable file
72
vendor/discord-php/http/src/Discord/Drivers/React.php
vendored
Executable file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Drivers;
|
||||
|
||||
use Discord\Http\DriverInterface;
|
||||
use Discord\Http\Request;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Http\Browser;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Socket\Connector;
|
||||
|
||||
/**
|
||||
* react/http driver for Discord HTTP client.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class React implements DriverInterface
|
||||
{
|
||||
/**
|
||||
* ReactPHP event loop.
|
||||
*
|
||||
* @var LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
/**
|
||||
* ReactPHP/HTTP browser.
|
||||
*
|
||||
* @var Browser
|
||||
*/
|
||||
protected $browser;
|
||||
|
||||
/**
|
||||
* Constructs the React driver.
|
||||
*
|
||||
* @param LoopInterface $loop
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(LoopInterface $loop, array $options = [])
|
||||
{
|
||||
$this->loop = $loop;
|
||||
|
||||
// Allow 400 and 500 HTTP requests to be resolved rather than rejected.
|
||||
$browser = new Browser($loop, new Connector($loop, $options));
|
||||
$this->browser = $browser->withRejectErrorResponse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the request using the React HTTP client.
|
||||
*
|
||||
* @param Request $request The request to run.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function runRequest($request): PromiseInterface
|
||||
{
|
||||
return $this->browser->{$request->getMethod()}(
|
||||
$request->getUrl(),
|
||||
$request->getHeaders(),
|
||||
$request->getContent()
|
||||
);
|
||||
}
|
||||
}
|
||||
368
vendor/discord-php/http/src/Discord/Endpoint.php
vendored
Executable file
368
vendor/discord-php/http/src/Discord/Endpoint.php
vendored
Executable file
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
class Endpoint implements EndpointInterface
|
||||
{
|
||||
use EndpointTrait;
|
||||
|
||||
// GET
|
||||
public const GATEWAY = 'gateway';
|
||||
// GET
|
||||
public const GATEWAY_BOT = self::GATEWAY.'/bot';
|
||||
|
||||
// GET
|
||||
public const APPLICATION_SKUS = 'applications/:application_id/skus';
|
||||
// GET, POST
|
||||
public const APPLICATION_EMOJIS = 'applications/:application_id/emojis';
|
||||
// GET, PATCH, DELETE
|
||||
public const APPLICATION_EMOJI = 'applications/:application_id/emojis/:emoji_id';
|
||||
// GET, POST
|
||||
public const APPLICATION_ENTITLEMENTS = 'applications/:application_id/entitlements';
|
||||
// DELETE
|
||||
public const APPLICATION_ENTITLEMENT = self::APPLICATION_ENTITLEMENTS.'/:entitlement_id';
|
||||
// POST
|
||||
public const APPLICATION_ENTITLEMENT_CONSUME = self::APPLICATION_ENTITLEMENT.'/consume';
|
||||
// GET, POST, PUT
|
||||
public const GLOBAL_APPLICATION_COMMANDS = 'applications/:application_id/commands';
|
||||
// GET, PATCH, DELETE
|
||||
public const GLOBAL_APPLICATION_COMMAND = self::GLOBAL_APPLICATION_COMMANDS.'/:command_id';
|
||||
// GET, POST, PUT
|
||||
public const GUILD_APPLICATION_COMMANDS = 'applications/:application_id/guilds/:guild_id/commands';
|
||||
// GET, PUT
|
||||
public const GUILD_APPLICATION_COMMANDS_PERMISSIONS = self::GUILD_APPLICATION_COMMANDS.'/permissions';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_APPLICATION_COMMAND = self::GUILD_APPLICATION_COMMANDS.'/:command_id';
|
||||
// GET, PUT
|
||||
public const GUILD_APPLICATION_COMMAND_PERMISSIONS = self::GUILD_APPLICATION_COMMANDS.'/:command_id/permissions';
|
||||
// POST
|
||||
public const INTERACTION_RESPONSE = 'interactions/:interaction_id/:interaction_token/callback';
|
||||
// POST
|
||||
public const CREATE_INTERACTION_FOLLOW_UP = 'webhooks/:application_id/:interaction_token';
|
||||
// PATCH, DELETE
|
||||
public const ORIGINAL_INTERACTION_RESPONSE = self::CREATE_INTERACTION_FOLLOW_UP.'/messages/@original';
|
||||
// PATCH, DELETE
|
||||
public const INTERACTION_FOLLOW_UP = self::CREATE_INTERACTION_FOLLOW_UP.'/messages/:message_id';
|
||||
|
||||
// GET
|
||||
public const SKU_SUBSCRIPTIONS = '/skus/:sku_id/subscriptions';
|
||||
// GET
|
||||
public const SKU_SUBSCRIPTION = self::SKU_SUBSCRIPTIONS.'/:subscription_id';
|
||||
|
||||
// GET
|
||||
public const AUDIT_LOG = 'guilds/:guild_id/audit-logs';
|
||||
|
||||
// GET, PATCH, DELETE
|
||||
public const CHANNEL = 'channels/:channel_id';
|
||||
// GET, POST
|
||||
public const CHANNEL_MESSAGES = self::CHANNEL.'/messages';
|
||||
// GET, PATCH, DELETE
|
||||
public const CHANNEL_MESSAGE = self::CHANNEL.'/messages/:message_id';
|
||||
// POST
|
||||
public const CHANNEL_CROSSPOST_MESSAGE = self::CHANNEL.'/messages/:message_id/crosspost';
|
||||
// POST
|
||||
public const CHANNEL_MESSAGES_BULK_DELETE = self::CHANNEL.'/messages/bulk-delete';
|
||||
// PUT, DELETE
|
||||
public const CHANNEL_PERMISSIONS = self::CHANNEL.'/permissions/:overwrite_id';
|
||||
// GET, POST
|
||||
public const CHANNEL_INVITES = self::CHANNEL.'/invites';
|
||||
// POST
|
||||
public const CHANNEL_FOLLOW = self::CHANNEL.'/followers';
|
||||
// POST
|
||||
public const CHANNEL_TYPING = self::CHANNEL.'/typing';
|
||||
// GET
|
||||
/** @deprecated Use `CHANNEL_MESSAGES_PINS` */
|
||||
public const CHANNEL_PINS = self::CHANNEL.'/pins';
|
||||
// PUT, DELETE
|
||||
/** @deprecated Use `CHANNEL_MESSAGES_PINS` */
|
||||
public const CHANNEL_PIN = self::CHANNEL.'/pins/:message_id';
|
||||
// GET
|
||||
public const CHANNEL_MESSAGES_PINS = self::CHANNEL.'/messages/pins';
|
||||
// PUT, DELETE
|
||||
public const CHANNEL_MESSAGES_PIN = self::CHANNEL.'/messages/pins/:message_id';
|
||||
// POST
|
||||
public const CHANNEL_THREADS = self::CHANNEL.'/threads';
|
||||
// POST
|
||||
public const CHANNEL_MESSAGE_THREADS = self::CHANNEL_MESSAGE.'/threads';
|
||||
// GET
|
||||
public const CHANNEL_THREADS_ARCHIVED_PUBLIC = self::CHANNEL_THREADS.'/archived/public';
|
||||
// GET
|
||||
public const CHANNEL_THREADS_ARCHIVED_PRIVATE = self::CHANNEL_THREADS.'/archived/private';
|
||||
// GET
|
||||
public const CHANNEL_THREADS_ARCHIVED_PRIVATE_ME = self::CHANNEL.'/users/@me/threads/archived/private';
|
||||
// POST
|
||||
public const CHANNEL_SEND_SOUNDBOARD_SOUND = self::CHANNEL.'/send-soundboard-sound';
|
||||
|
||||
// GET, PATCH, DELETE
|
||||
public const THREAD = 'channels/:thread_id';
|
||||
// GET
|
||||
public const THREAD_MEMBERS = self::THREAD.'/thread-members';
|
||||
// GET, PUT, DELETE
|
||||
public const THREAD_MEMBER = self::THREAD_MEMBERS.'/:user_id';
|
||||
// PUT, DELETE
|
||||
public const THREAD_MEMBER_ME = self::THREAD_MEMBERS.'/@me';
|
||||
|
||||
// GET, DELETE
|
||||
public const MESSAGE_REACTION_ALL = self::CHANNEL.'/messages/:message_id/reactions';
|
||||
// GET, DELETE
|
||||
public const MESSAGE_REACTION_EMOJI = self::CHANNEL.'/messages/:message_id/reactions/:emoji';
|
||||
// PUT, DELETE
|
||||
public const OWN_MESSAGE_REACTION = self::CHANNEL.'/messages/:message_id/reactions/:emoji/@me';
|
||||
// DELETE
|
||||
public const USER_MESSAGE_REACTION = self::CHANNEL.'/messages/:message_id/reactions/:emoji/:user_id';
|
||||
|
||||
// GET
|
||||
protected const MESSAGE_POLL = self::CHANNEL.'/polls/:message_id';
|
||||
// GET
|
||||
public const MESSAGE_POLL_ANSWER = self::MESSAGE_POLL.'/answers/:answer_id';
|
||||
// POST
|
||||
public const MESSAGE_POLL_EXPIRE = self::MESSAGE_POLL.'/expire';
|
||||
|
||||
// GET, POST
|
||||
public const CHANNEL_WEBHOOKS = self::CHANNEL.'/webhooks';
|
||||
|
||||
// POST
|
||||
public const GUILDS = 'guilds';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD = 'guilds/:guild_id';
|
||||
// GET, POST, PATCH
|
||||
public const GUILD_CHANNELS = self::GUILD.'/channels';
|
||||
// GET
|
||||
public const GUILD_THREADS_ACTIVE = self::GUILD.'/threads/active';
|
||||
// GET
|
||||
public const GUILD_MESSAGES_SEARCH = self::GUILD.'/messages/search';
|
||||
|
||||
// GET
|
||||
public const GUILD_MEMBERS = self::GUILD.'/members';
|
||||
// GET
|
||||
public const GUILD_MEMBERS_SEARCH = self::GUILD.'/members/search';
|
||||
// GET, PATCH, PUT, DELETE
|
||||
public const GUILD_MEMBER = self::GUILD.'/members/:user_id';
|
||||
// PATCH
|
||||
public const GUILD_MEMBER_SELF = self::GUILD.'/members/@me';
|
||||
/** @deprecated 9.0.9 Use `GUILD_MEMBER_SELF` */
|
||||
public const GUILD_MEMBER_SELF_NICK = self::GUILD.'/members/@me/nick';
|
||||
// PUT, DELETE
|
||||
public const GUILD_MEMBER_ROLE = self::GUILD.'/members/:user_id/roles/:role_id';
|
||||
|
||||
// GET
|
||||
public const GUILD_BANS = self::GUILD.'/bans';
|
||||
// GET, PUT, DELETE
|
||||
public const GUILD_BAN = self::GUILD.'/bans/:user_id';
|
||||
// POST
|
||||
public const GUILD_BAN_BULK = self::GUILD.'/bulk-ban';
|
||||
|
||||
// GET, PATCH
|
||||
public const GUILD_ROLES = self::GUILD.'/roles';
|
||||
// GET
|
||||
public const GUILD_ROLES_MEMBER_COUNTS = self::GUILD.'/roles/member-counts';
|
||||
// GET, POST, PATCH, DELETE
|
||||
public const GUILD_ROLE = self::GUILD.'/roles/:role_id';
|
||||
|
||||
// POST
|
||||
public const GUILD_MFA = self::GUILD.'/mfa';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_INVITES = self::GUILD.'/invites';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_INTEGRATIONS = self::GUILD.'/integrations';
|
||||
// PATCH, DELETE
|
||||
public const GUILD_INTEGRATION = self::GUILD.'/integrations/:integration_id';
|
||||
// POST
|
||||
public const GUILD_INTEGRATION_SYNC = self::GUILD.'/integrations/:integration_id/sync';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_EMOJIS = self::GUILD.'/emojis';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_EMOJI = self::GUILD.'/emojis/:emoji_id';
|
||||
|
||||
// GET
|
||||
public const GUILD_PREVIEW = self::GUILD.'/preview';
|
||||
// GET, POST
|
||||
public const GUILD_PRUNE = self::GUILD.'/prune';
|
||||
// GET
|
||||
public const GUILD_REGIONS = self::GUILD.'/regions';
|
||||
// GET, PATCH
|
||||
public const GUILD_WIDGET_SETTINGS = self::GUILD.'/widget';
|
||||
// GET
|
||||
public const GUILD_WIDGET = self::GUILD.'/widget.json';
|
||||
// GET
|
||||
public const GUILD_WIDGET_IMAGE = self::GUILD.'/widget.png';
|
||||
// GET, PATCH
|
||||
public const GUILD_WELCOME_SCREEN = self::GUILD.'/welcome-screen';
|
||||
// GET
|
||||
public const GUILD_ONBOARDING = self::GUILD.'/onboarding';
|
||||
// GET
|
||||
public const LIST_VOICE_REGIONS = 'voice/regions';
|
||||
// GET, PATCH
|
||||
public const GUILD_USER_CURRENT_VOICE_STATE = self::GUILD.'/voice-states/@me';
|
||||
// GET, PATCH
|
||||
public const GUILD_USER_VOICE_STATE = self::GUILD.'/voice-states/:user_id';
|
||||
// GET
|
||||
public const GUILD_VANITY_URL = self::GUILD.'/vanity-url';
|
||||
// GET, PATCH
|
||||
public const GUILD_MEMBERSHIP_SCREENING = self::GUILD.'/member-verification';
|
||||
// GET
|
||||
public const GUILD_WEBHOOKS = self::GUILD.'/webhooks';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_STICKERS = self::GUILD.'/stickers';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_STICKER = self::GUILD.'/stickers/:sticker_id';
|
||||
|
||||
// GET
|
||||
public const STICKER = 'stickers/:sticker_id';
|
||||
// GET
|
||||
public const STICKER_PACKS = 'sticker-packs';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_SCHEDULED_EVENTS = self::GUILD.'/scheduled-events';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_SCHEDULED_EVENT = self::GUILD.'/scheduled-events/:guild_scheduled_event_id';
|
||||
// GET
|
||||
public const GUILD_SCHEDULED_EVENT_USERS = self::GUILD.'/scheduled-events/:guild_scheduled_event_id/users';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_SOUNDBOARD_SOUNDS = self::GUILD.'/soundboard-sounds';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_SOUNDBOARD_SOUND = self::GUILD.'/soundboard-sounds/:sound_id';
|
||||
|
||||
// GET, DELETE
|
||||
public const INVITE = 'invites/:code';
|
||||
|
||||
// POST
|
||||
public const STAGE_INSTANCES = 'stage-instances';
|
||||
// GET, PATCH, DELETE
|
||||
public const STAGE_INSTANCE = 'stage-instances/:channel_id';
|
||||
|
||||
// GET, POST
|
||||
public const GUILDS_TEMPLATE = self::GUILDS.'/templates/:template_code';
|
||||
// GET, POST
|
||||
public const GUILD_TEMPLATES = self::GUILD.'/templates';
|
||||
// PUT, PATCH, DELETE
|
||||
public const GUILD_TEMPLATE = self::GUILD.'/templates/:template_code';
|
||||
|
||||
// GET, POST
|
||||
public const GUILD_AUTO_MODERATION_RULES = self::GUILD.'/auto-moderation/rules';
|
||||
// GET, PATCH, DELETE
|
||||
public const GUILD_AUTO_MODERATION_RULE = self::GUILD.'/auto-moderation/rules/:auto_moderation_rule_id';
|
||||
|
||||
// POST
|
||||
public const LOBBIES = 'lobbies';
|
||||
// GET, PATCH, DELETE
|
||||
public const LOBBY = self::LOBBIES.'/:lobby_id';
|
||||
// PUT, DELETE
|
||||
public const LOBBY_MEMBER = self::LOBBY.'/members/:user_id/';
|
||||
// DELETE
|
||||
public const LOBBY_SELF = self::LOBBY.'/members/@me';
|
||||
// PATCH
|
||||
public const LOBBY_CHANNEL_LINKING = self::LOBBY.'/channel-linking';
|
||||
|
||||
// GET
|
||||
public const SOUNDBOARD_DEFAULT_SOUNDS = 'soundboard-default-sounds';
|
||||
|
||||
// GET, PATCH
|
||||
public const USER_CURRENT = 'users/@me';
|
||||
// GET
|
||||
public const USER = 'users/:user_id';
|
||||
// GET
|
||||
public const USER_CURRENT_GUILDS = self::USER_CURRENT.'/guilds';
|
||||
// DELETE
|
||||
public const USER_CURRENT_GUILD = self::USER_CURRENT.'/guilds/:guild_id';
|
||||
// GET
|
||||
public const USER_CURRENT_MEMBER = self::USER_CURRENT_GUILD.'/member';
|
||||
// GET, POST
|
||||
public const USER_CURRENT_CHANNELS = self::USER_CURRENT.'/channels';
|
||||
// GET
|
||||
public const USER_CURRENT_CONNECTIONS = self::USER_CURRENT.'/connections';
|
||||
// GET, PUT
|
||||
public const USER_CURRENT_APPLICATION_ROLE_CONNECTION = self::USER_CURRENT.'/applications/:application_id/role-connection';
|
||||
// GET, PATCH
|
||||
public const APPLICATION_CURRENT = 'applications/@me';
|
||||
// GET
|
||||
public const APPLICATION_ACTIVITY_INSTANCE = 'applications/:application_id/activity-instances/:instance_id';
|
||||
|
||||
// GET, PATCH, DELETE
|
||||
public const WEBHOOK = 'webhooks/:webhook_id';
|
||||
// GET, PATCH, DELETE
|
||||
public const WEBHOOK_TOKEN = 'webhooks/:webhook_id/:webhook_token';
|
||||
// POST
|
||||
public const WEBHOOK_EXECUTE = self::WEBHOOK_TOKEN;
|
||||
// POST
|
||||
public const WEBHOOK_EXECUTE_SLACK = self::WEBHOOK_EXECUTE.'/slack';
|
||||
// POST
|
||||
public const WEBHOOK_EXECUTE_GITHUB = self::WEBHOOK_EXECUTE.'/github';
|
||||
// PATCH, DELETE
|
||||
public const WEBHOOK_MESSAGE = self::WEBHOOK_TOKEN.'/messages/:message_id';
|
||||
|
||||
// GET, PUT
|
||||
public const APPLICATION_ROLE_CONNECTION_METADATA = 'applications/:application_id/role-connections/metadata';
|
||||
|
||||
/**
|
||||
* Regex to identify parameters in endpoints.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const REGEX = '/:([^\/]*)/';
|
||||
|
||||
/**
|
||||
* A list of parameters considered 'major' by Discord.
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/rate-limits
|
||||
* @var string[]
|
||||
*/
|
||||
public const MAJOR_PARAMETERS = ['channel_id', 'guild_id', 'webhook_id', 'thread_id'];
|
||||
|
||||
/**
|
||||
* The string version of the endpoint, including all parameters.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $endpoint;
|
||||
|
||||
/**
|
||||
* Array of placeholders to be replaced in the endpoint.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $vars = [];
|
||||
|
||||
/**
|
||||
* Array of arguments to substitute into the endpoint.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $args = [];
|
||||
|
||||
/**
|
||||
* Array of query data to be appended
|
||||
* to the end of the endpoint with `http_build_query`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $query = [];
|
||||
|
||||
/**
|
||||
* Creates an endpoint class.
|
||||
*
|
||||
* @param string $endpoint
|
||||
*/
|
||||
public function __construct(string $endpoint)
|
||||
{
|
||||
$this->endpoint = $endpoint;
|
||||
|
||||
if (preg_match_all(self::REGEX, $endpoint, $vars)) {
|
||||
$this->vars = $vars[1] ?? [];
|
||||
}
|
||||
}
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/EndpointInterface.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/EndpointInterface.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
interface EndpointInterface
|
||||
{
|
||||
public function bindArgs(...$args): self;
|
||||
public function bindAssoc(array $args): self;
|
||||
public function addQuery(string $key, $value): void;
|
||||
public function toAbsoluteEndpoint(bool $onlyMajorParameters = false): string;
|
||||
public function __toString(): string;
|
||||
public static function bind(string $endpoint, ...$args);
|
||||
}
|
||||
137
vendor/discord-php/http/src/Discord/EndpointTrait.php
vendored
Executable file
137
vendor/discord-php/http/src/Discord/EndpointTrait.php
vendored
Executable file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
trait EndpointTrait
|
||||
{
|
||||
/**
|
||||
* Binds a list of arguments to the endpoint.
|
||||
*
|
||||
* @param string[] ...$args
|
||||
* @return this
|
||||
*/
|
||||
public function bindArgs(...$args): self
|
||||
{
|
||||
for ($i = 0; $i < count($this->vars) && $i < count($args); $i++) {
|
||||
$this->args[$this->vars[$i]] = $args[$i];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds an associative array to the endpoint.
|
||||
*
|
||||
* @param string[] $args
|
||||
* @return this
|
||||
*/
|
||||
public function bindAssoc(array $args): self
|
||||
{
|
||||
$this->args = array_merge($this->args, $args);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key-value query pair to the endpoint.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|bool $value
|
||||
*/
|
||||
public function addQuery(string $key, $value): void
|
||||
{
|
||||
if (! is_bool($value)) {
|
||||
$value = is_array($value)
|
||||
? (implode(' ', $value))
|
||||
: (string) $value;
|
||||
}
|
||||
|
||||
$this->query[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the endpoint into the absolute endpoint with
|
||||
* placeholders replaced.
|
||||
*
|
||||
* Passing a true boolean in will only replace the major parameters.
|
||||
* Used for rate limit buckets.
|
||||
*
|
||||
* @param bool $onlyMajorParameters
|
||||
* @return string
|
||||
*/
|
||||
public function toAbsoluteEndpoint(bool $onlyMajorParameters = false): string
|
||||
{
|
||||
$endpoint = $this->endpoint;
|
||||
|
||||
// Process in order of longest to shortest variable name to prevent partial replacements (see #16).
|
||||
$vars = $this->vars;
|
||||
usort($vars, fn ($a, $b) => strlen($b) <=> strlen($a));
|
||||
|
||||
foreach ($vars as $var) {
|
||||
if (
|
||||
! isset($this->args[$var]) ||
|
||||
(
|
||||
$onlyMajorParameters &&
|
||||
(method_exists($this, 'isMajorParameter') ? ! $this->isMajorParameter($var) : false)
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$endpoint = str_replace(":{$var}", $this->args[$var], $endpoint);
|
||||
}
|
||||
|
||||
if (! $onlyMajorParameters && count($this->query) > 0) {
|
||||
$endpoint .= '?'.http_build_query($this->query);
|
||||
}
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the endpoint to a string.
|
||||
* Alias of ->toAbsoluteEndpoint();.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toAbsoluteEndpoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an endpoint class and binds arguments to
|
||||
* the newly created instance.
|
||||
*
|
||||
* @param string $endpoint
|
||||
* @param string[] $args
|
||||
* @return Endpoint
|
||||
*/
|
||||
public static function bind(string $endpoint, ...$args)
|
||||
{
|
||||
$endpoint = new Endpoint($endpoint);
|
||||
$endpoint->bindArgs(...$args);
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a parameter is a major parameter.
|
||||
*
|
||||
* @param string $param
|
||||
* @return bool
|
||||
*/
|
||||
private static function isMajorParameter(string $param): bool
|
||||
{
|
||||
return in_array($param, Endpoint::MAJOR_PARAMETERS);
|
||||
}
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/BadRequestException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/BadRequestException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when a request to Discord's REST API returned ClientErrorResponse.
|
||||
*
|
||||
* @author SQKo
|
||||
*/
|
||||
class BadRequestException extends RequestFailedException
|
||||
{
|
||||
protected $code = 400;
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/ContentTooLongException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/ContentTooLongException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when the Discord servers return `content longer than 2000 characters` after
|
||||
* a REST request. The user must use WebSockets to obtain this data if they need it.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class ContentTooLongException extends RequestFailedException
|
||||
{
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/InvalidTokenException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/InvalidTokenException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when an invalid token is provided to a Discord endpoint.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class InvalidTokenException extends RequestFailedException
|
||||
{
|
||||
protected $code = 401;
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/MethodNotAllowedException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/MethodNotAllowedException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when a request to Discord's REST API method is invalid.
|
||||
*
|
||||
* @author SQKo
|
||||
*/
|
||||
class MethodNotAllowedException extends RequestFailedException
|
||||
{
|
||||
protected $code = 405;
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/NoPermissionsException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/NoPermissionsException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when you do not have permissions to do something.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class NoPermissionsException extends RequestFailedException
|
||||
{
|
||||
protected $code = 403;
|
||||
}
|
||||
22
vendor/discord-php/http/src/Discord/Exceptions/NotFoundException.php
vendored
Executable file
22
vendor/discord-php/http/src/Discord/Exceptions/NotFoundException.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when a 404 Not Found response is received.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class NotFoundException extends RequestFailedException
|
||||
{
|
||||
protected $code = 404;
|
||||
}
|
||||
23
vendor/discord-php/http/src/Discord/Exceptions/RateLimitException.php
vendored
Executable file
23
vendor/discord-php/http/src/Discord/Exceptions/RateLimitException.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when a request to Discord's REST API got rate limited and the library
|
||||
* does not know how to handle.
|
||||
*
|
||||
* @author SQKo
|
||||
*/
|
||||
class RateLimitException extends RequestFailedException
|
||||
{
|
||||
protected $code = 429;
|
||||
}
|
||||
23
vendor/discord-php/http/src/Discord/Exceptions/RequestFailedException.php
vendored
Executable file
23
vendor/discord-php/http/src/Discord/Exceptions/RequestFailedException.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Thrown when a request to Discord's REST API fails.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class RequestFailedException extends RuntimeException
|
||||
{
|
||||
}
|
||||
152
vendor/discord-php/http/src/Discord/Http.php
vendored
Executable file
152
vendor/discord-php/http/src/Discord/Http.php
vendored
Executable file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use SplQueue;
|
||||
|
||||
/**
|
||||
* Discord HTTP client.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class Http implements HttpInterface
|
||||
{
|
||||
use HttpTrait;
|
||||
|
||||
/**
|
||||
* DiscordPHP-Http version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const VERSION = 'v10.5.1';
|
||||
|
||||
/**
|
||||
* Current Discord HTTP API version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HTTP_API_VERSION = 10;
|
||||
|
||||
/**
|
||||
* Discord API base URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const BASE_URL = 'https://discord.com/api/v'.self::HTTP_API_VERSION;
|
||||
|
||||
/**
|
||||
* The number of concurrent requests which can
|
||||
* be executed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const CONCURRENT_REQUESTS = 5;
|
||||
|
||||
/**
|
||||
* Authentication token.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* Logger for HTTP requests.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* HTTP driver.
|
||||
*
|
||||
* @var DriverInterface
|
||||
*/
|
||||
protected $driver;
|
||||
|
||||
/**
|
||||
* ReactPHP event loop.
|
||||
*
|
||||
* @var LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
/**
|
||||
* Array of request buckets.
|
||||
*
|
||||
* @var Bucket[]
|
||||
*/
|
||||
protected $buckets = [];
|
||||
|
||||
/**
|
||||
* The current rate-limit.
|
||||
*
|
||||
* @var RateLimit
|
||||
*/
|
||||
protected $rateLimit;
|
||||
|
||||
/**
|
||||
* Timer that resets the current global rate-limit.
|
||||
*
|
||||
* @var TimerInterface
|
||||
*/
|
||||
protected $rateLimitReset;
|
||||
|
||||
/**
|
||||
* Request queue to prevent API
|
||||
* overload.
|
||||
*
|
||||
* @var SplQueue
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* Request queue to prevent API
|
||||
* overload.
|
||||
*
|
||||
* @var SplQueue
|
||||
*/
|
||||
protected $unboundQueue;
|
||||
|
||||
/**
|
||||
* Number of requests that are waiting for a response.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $waiting = 0;
|
||||
|
||||
/**
|
||||
* Whether react/promise v3 is used, if false, using v2.
|
||||
*/
|
||||
protected $promiseV3 = true;
|
||||
|
||||
/**
|
||||
* Http wrapper constructor.
|
||||
*
|
||||
* @param string $token
|
||||
* @param LoopInterface $loop
|
||||
* @param DriverInterface|null $driver
|
||||
*/
|
||||
public function __construct(string $token, LoopInterface $loop, LoggerInterface $logger, ?DriverInterface $driver = null)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->loop = $loop;
|
||||
$this->logger = $logger;
|
||||
$this->driver = $driver;
|
||||
$this->queue = new SplQueue;
|
||||
$this->unboundQueue = new SplQueue;
|
||||
|
||||
$this->promiseV3 = str_starts_with(InstalledVersions::getVersion('react/promise'), '3.');
|
||||
}
|
||||
}
|
||||
34
vendor/discord-php/http/src/Discord/HttpInterface.php
vendored
Executable file
34
vendor/discord-php/http/src/Discord/HttpInterface.php
vendored
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Discord HTTP client.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
interface HttpInterface
|
||||
{
|
||||
public function setDriver(DriverInterface $driver): void;
|
||||
public function get($url, $content = null, array $headers = []): PromiseInterface;
|
||||
public function post($url, $content = null, array $headers = []): PromiseInterface;
|
||||
public function put($url, $content = null, array $headers = []): PromiseInterface;
|
||||
public function patch($url, $content = null, array $headers = []): PromiseInterface;
|
||||
public function delete($url, $content = null, array $headers = []): PromiseInterface;
|
||||
public function queueRequest(string $method, Endpoint $url, $content, array $headers = []): PromiseInterface;
|
||||
public static function isUnboundEndpoint(Request $request): bool;
|
||||
public function handleError(ResponseInterface $response): \Throwable;
|
||||
public function getUserAgent(): string;
|
||||
}
|
||||
480
vendor/discord-php/http/src/Discord/HttpTrait.php
vendored
Executable file
480
vendor/discord-php/http/src/Discord/HttpTrait.php
vendored
Executable file
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use Discord\Http\Exceptions\BadRequestException;
|
||||
use Discord\Http\Exceptions\ContentTooLongException;
|
||||
use Discord\Http\Exceptions\InvalidTokenException;
|
||||
use Discord\Http\Exceptions\MethodNotAllowedException;
|
||||
use Discord\Http\Exceptions\NoPermissionsException;
|
||||
use Discord\Http\Exceptions\NotFoundException;
|
||||
use Discord\Http\Exceptions\RateLimitException;
|
||||
use Discord\Http\Exceptions\RequestFailedException;
|
||||
use Discord\Http\Multipart\MultipartBody;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Discord HTTP client.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
trait HttpTrait
|
||||
{
|
||||
/**
|
||||
* Sets the driver of the HTTP client.
|
||||
*
|
||||
* @param DriverInterface $driver
|
||||
*/
|
||||
public function setDriver(DriverInterface $driver): void
|
||||
{
|
||||
$this->driver = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a GET request.
|
||||
*
|
||||
* @param string|Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function get($url, $content = null, array $headers = []): PromiseInterface
|
||||
{
|
||||
if (! ($url instanceof Endpoint)) {
|
||||
$url = Endpoint::bind($url);
|
||||
}
|
||||
|
||||
return $this->queueRequest('get', $url, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a POST request.
|
||||
*
|
||||
* @param string|Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function post($url, $content = null, array $headers = []): PromiseInterface
|
||||
{
|
||||
if (! ($url instanceof Endpoint)) {
|
||||
$url = Endpoint::bind($url);
|
||||
}
|
||||
|
||||
return $this->queueRequest('post', $url, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a PUT request.
|
||||
*
|
||||
* @param string|Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function put($url, $content = null, array $headers = []): PromiseInterface
|
||||
{
|
||||
if (! ($url instanceof Endpoint)) {
|
||||
$url = Endpoint::bind($url);
|
||||
}
|
||||
|
||||
return $this->queueRequest('put', $url, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a PATCH request.
|
||||
*
|
||||
* @param string|Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function patch($url, $content = null, array $headers = []): PromiseInterface
|
||||
{
|
||||
if (! ($url instanceof Endpoint)) {
|
||||
$url = Endpoint::bind($url);
|
||||
}
|
||||
|
||||
return $this->queueRequest('patch', $url, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a DELETE request.
|
||||
*
|
||||
* @param string|Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function delete($url, $content = null, array $headers = []): PromiseInterface
|
||||
{
|
||||
if (! ($url instanceof Endpoint)) {
|
||||
$url = Endpoint::bind($url);
|
||||
}
|
||||
|
||||
return $this->queueRequest('delete', $url, $content, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and queues a request.
|
||||
*
|
||||
* @param string $method
|
||||
* @param Endpoint $url
|
||||
* @param mixed $content
|
||||
* @param array $headers
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public function queueRequest(string $method, Endpoint $url, $content, array $headers = []): PromiseInterface
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
if (is_null($this->driver)) {
|
||||
$deferred->reject(new \Exception('HTTP driver is missing.'));
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
$headers = array_merge($headers, [
|
||||
'User-Agent' => $this->getUserAgent(),
|
||||
'Authorization' => $this->token,
|
||||
'X-Ratelimit-Precision' => 'millisecond',
|
||||
]);
|
||||
|
||||
$baseHeaders = [
|
||||
'User-Agent' => $this->getUserAgent(),
|
||||
'Authorization' => $this->token,
|
||||
'X-Ratelimit-Precision' => 'millisecond',
|
||||
];
|
||||
|
||||
if (! is_null($content) && ! isset($headers['Content-Type'])) {
|
||||
$baseHeaders = array_merge(
|
||||
$baseHeaders,
|
||||
$this->guessContent($content)
|
||||
);
|
||||
}
|
||||
|
||||
$headers = array_merge($baseHeaders, $headers);
|
||||
|
||||
$request = new Request($deferred, $method, $url, $content ?? '', $headers);
|
||||
$this->sortIntoBucket($request);
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses the headers and transforms the content of a request.
|
||||
*
|
||||
* @param mixed $content
|
||||
*/
|
||||
protected function guessContent(&$content)
|
||||
{
|
||||
if ($content instanceof MultipartBody) {
|
||||
$headers = $content->getHeaders();
|
||||
$content = (string) $content;
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
$content = json_encode($content);
|
||||
|
||||
return [
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Length' => strlen($content),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Deferred|null $deferred
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
protected function executeRequest(Request $request, ?Deferred $deferred = null): PromiseInterface
|
||||
{
|
||||
if ($deferred === null) {
|
||||
$deferred = new Deferred();
|
||||
}
|
||||
|
||||
if ($this->rateLimit) {
|
||||
$deferred->reject($this->rateLimit);
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
// Promises v3 changed `->then` to behave as `->done` and removed `->then`. We still need the behaviour of `->done` in projects using v2
|
||||
$this->driver->runRequest($request)->{$this->promiseV3 ? 'then' : 'done'}(function (ResponseInterface $response) use ($request, $deferred) {
|
||||
$data = json_decode((string) $response->getBody());
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
// Discord Rate-limit
|
||||
if ($statusCode == 429) {
|
||||
if (! isset($data->global)) {
|
||||
if ($response->hasHeader('X-RateLimit-Global')) {
|
||||
$data->global = $response->getHeader('X-RateLimit-Global')[0] == 'true';
|
||||
} else {
|
||||
// Some other 429
|
||||
$this->logger->error($request.' does not contain global rate-limit value');
|
||||
$rateLimitError = new RateLimitException('No rate limit global response', $statusCode);
|
||||
$deferred->reject($rateLimitError);
|
||||
$request->getDeferred()->reject($rateLimitError);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($data->retry_after)) {
|
||||
if ($response->hasHeader('Retry-After')) {
|
||||
$data->retry_after = $response->getHeader('Retry-After')[0];
|
||||
} else {
|
||||
// Some other 429
|
||||
$this->logger->error($request.' does not contain retry after rate-limit value');
|
||||
$rateLimitError = new RateLimitException('No rate limit retry after response', $statusCode);
|
||||
$deferred->reject($rateLimitError);
|
||||
$request->getDeferred()->reject($rateLimitError);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$rateLimit = new RateLimit($data->global, $data->retry_after);
|
||||
$this->logger->warning($request.' hit rate-limit: '.$rateLimit);
|
||||
|
||||
if ($rateLimit->isGlobal() && ! $this->rateLimit) {
|
||||
$this->rateLimit = $rateLimit;
|
||||
$this->rateLimitReset = $this->loop->addTimer($rateLimit->getRetryAfter(), function () {
|
||||
$this->rateLimit = null;
|
||||
$this->rateLimitReset = null;
|
||||
$this->logger->info('global rate-limit reset');
|
||||
|
||||
// Loop through all buckets and check for requests
|
||||
foreach ($this->buckets as $bucket) {
|
||||
$bucket->checkQueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$deferred->reject($rateLimit->isGlobal() ? $this->rateLimit : $rateLimit);
|
||||
}
|
||||
// Bad Gateway
|
||||
// Cloudflare SSL Handshake error
|
||||
// Push to the back of the bucket to be retried.
|
||||
elseif ($statusCode == 502 || $statusCode == 525) {
|
||||
$this->logger->warning($request.' 502/525 - retrying request');
|
||||
|
||||
$this->executeRequest($request, $deferred);
|
||||
}
|
||||
// Any other unsuccessful status codes
|
||||
elseif ($statusCode < 200 || $statusCode >= 300) {
|
||||
$error = $this->handleError($response);
|
||||
$this->logger->warning($request.' failed: '.$error);
|
||||
|
||||
$deferred->reject($error);
|
||||
$request->getDeferred()->reject($error);
|
||||
}
|
||||
// All is well
|
||||
else {
|
||||
$this->logger->debug($request.' successful');
|
||||
|
||||
$deferred->resolve($response);
|
||||
$request->getDeferred()->resolve($data);
|
||||
}
|
||||
}, function (\Exception $e) use ($request, $deferred) {
|
||||
$this->logger->warning($request.' failed: '.$e->getMessage());
|
||||
|
||||
$deferred->reject($e);
|
||||
$request->getDeferred()->reject($e);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a request into a bucket.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
protected function sortIntoBucket(Request $request): void
|
||||
{
|
||||
$bucket = $this->getBucket($request->getBucketID());
|
||||
$bucket->enqueue($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a bucket.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return Bucket
|
||||
*/
|
||||
protected function getBucket(string $key): Bucket
|
||||
{
|
||||
if (! isset($this->buckets[$key])) {
|
||||
$bucket = new Bucket($key, $this->loop, $this->logger, function (Request $request) {
|
||||
$deferred = new Deferred();
|
||||
self::isUnboundEndpoint($request)
|
||||
? $this->unboundQueue->enqueue([$request, $deferred])
|
||||
: $this->queue->enqueue([$request, $deferred]);
|
||||
$this->checkQueue();
|
||||
|
||||
return $deferred->promise();
|
||||
});
|
||||
|
||||
$this->buckets[$key] = $bucket;
|
||||
}
|
||||
|
||||
return $this->buckets[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the request queue to see if more requests can be
|
||||
* sent out.
|
||||
*/
|
||||
protected function checkQueue(bool $check_interactions = true): void
|
||||
{
|
||||
if ($check_interactions) {
|
||||
$this->checkunboundQueue();
|
||||
}
|
||||
|
||||
if ($this->waiting >= Http::CONCURRENT_REQUESTS || $this->queue->isEmpty()) {
|
||||
$this->logger->debug('http not checking queue', ['waiting' => $this->waiting, 'empty' => $this->queue->isEmpty()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Request $request
|
||||
* @var Deferred $deferred
|
||||
*/
|
||||
[$request, $deferred] = $this->queue->dequeue();
|
||||
++$this->waiting;
|
||||
|
||||
$this->executeRequest($request)->then(function ($result) use ($deferred) {
|
||||
--$this->waiting;
|
||||
$this->checkQueue(false);
|
||||
$deferred->resolve($result);
|
||||
}, function ($e) use ($deferred) {
|
||||
--$this->waiting;
|
||||
$this->checkQueue(false);
|
||||
$deferred->reject($e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the interaction queue to see if more requests can be
|
||||
* sent out.
|
||||
*/
|
||||
protected function checkunboundQueue(): void
|
||||
{
|
||||
if ($this->unboundQueue->isEmpty()) {
|
||||
$this->logger->debug('http not checking interaction queue', ['waiting' => $this->waiting, 'empty' => $this->unboundQueue->isEmpty()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Request $request
|
||||
* @var Deferred $deferred
|
||||
*/
|
||||
[$request, $deferred] = $this->unboundQueue->dequeue();
|
||||
|
||||
$this->executeRequest($request)->then(function ($result) use ($deferred) {
|
||||
$this->checkQueue();
|
||||
$deferred->resolve($result);
|
||||
}, function ($e) use ($deferred) {
|
||||
$this->checkQueue();
|
||||
$deferred->reject($e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the request is for an endpoint not bound by the global rate limit.
|
||||
*
|
||||
* @link https://discord.com/developers/docs/interactions/receiving-and-responding#endpoints
|
||||
*
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUnboundEndpoint(Request $request): bool
|
||||
{
|
||||
$url = $request->getUrl();
|
||||
|
||||
return
|
||||
(strpos($url, '/interactions') === 0 && strpos($url, '/callback') !== false)
|
||||
|| strpos($url, '/webhooks') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an exception based on the request.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return \Throwable
|
||||
*/
|
||||
public function handleError(ResponseInterface $response): \Throwable
|
||||
{
|
||||
$reason = $response->getReasonPhrase().' - ';
|
||||
|
||||
$errorBody = (string) $response->getBody();
|
||||
$errorCode = $response->getStatusCode();
|
||||
|
||||
// attempt to prettyify the response content
|
||||
if (($content = json_decode($errorBody)) !== null) {
|
||||
if (! empty($content->code)) {
|
||||
$errorCode = $content->code;
|
||||
}
|
||||
$reason .= json_encode($content, JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
$reason .= $errorBody;
|
||||
}
|
||||
|
||||
switch ($response->getStatusCode()) {
|
||||
case 400:
|
||||
return new BadRequestException($reason, $errorCode);
|
||||
case 401:
|
||||
return new InvalidTokenException($reason, $errorCode);
|
||||
case 403:
|
||||
return new NoPermissionsException($reason, $errorCode);
|
||||
case 404:
|
||||
return new NotFoundException($reason, $errorCode);
|
||||
case 405:
|
||||
return new MethodNotAllowedException($reason, $errorCode);
|
||||
case 500:
|
||||
if (strpos(strtolower($errorBody), 'longer than 2000 characters') !== false ||
|
||||
strpos(strtolower($errorBody), 'string value is too long') !== false) {
|
||||
// Response was longer than 2000 characters and was blocked by Discord.
|
||||
return new ContentTooLongException('Response was more than 2000 characters. Use another method to get this data.', $errorCode);
|
||||
}
|
||||
default:
|
||||
return new RequestFailedException($reason, $errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the User-Agent of the HTTP client.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return 'DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, '.Http::VERSION.')';
|
||||
}
|
||||
}
|
||||
58
vendor/discord-php/http/src/Discord/Multipart/MultipartBody.php
vendored
Executable file
58
vendor/discord-php/http/src/Discord/Multipart/MultipartBody.php
vendored
Executable file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Multipart;
|
||||
|
||||
class MultipartBody
|
||||
{
|
||||
public const BOUNDARY = 'DISCORDPHP-HTTP-BOUNDARY';
|
||||
|
||||
private array $fields;
|
||||
public string $boundary;
|
||||
|
||||
/**
|
||||
* @var MultipartField[]
|
||||
*/
|
||||
public function __construct(array $fields, ?string $boundary = null)
|
||||
{
|
||||
$this->fields = $fields;
|
||||
$this->boundary = $boundary ?? self::BOUNDARY;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$prefixedBoundary = '--'.$this->boundary;
|
||||
$boundaryEnd = $prefixedBoundary.'--';
|
||||
|
||||
$convertedFields = array_map(
|
||||
function (MultipartField $field) {
|
||||
return (string) $field;
|
||||
},
|
||||
$this->fields
|
||||
);
|
||||
|
||||
$fieldsString = implode(PHP_EOL.$prefixedBoundary.PHP_EOL, $convertedFields);
|
||||
|
||||
return implode(PHP_EOL, [
|
||||
$prefixedBoundary,
|
||||
$fieldsString,
|
||||
$boundaryEnd,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return [
|
||||
'Content-Type' => 'multipart/form-data; boundary='.$this->boundary,
|
||||
'Content-Length' => strlen((string) $this),
|
||||
];
|
||||
}
|
||||
}
|
||||
54
vendor/discord-php/http/src/Discord/Multipart/MultipartField.php
vendored
Executable file
54
vendor/discord-php/http/src/Discord/Multipart/MultipartField.php
vendored
Executable file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http\Multipart;
|
||||
|
||||
class MultipartField
|
||||
{
|
||||
private string $name;
|
||||
private string $content;
|
||||
private array $headers;
|
||||
private ?string $fileName;
|
||||
|
||||
/**
|
||||
* @var String[]
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $content,
|
||||
array $headers = [],
|
||||
?string $fileName = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->content = $content;
|
||||
$this->headers = $headers;
|
||||
$this->fileName = $fileName;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$out = 'Content-Disposition: form-data; name="'.$this->name.'"';
|
||||
|
||||
if (! is_null($this->fileName)) {
|
||||
$out .= '; filename="'.urlencode($this->fileName).'"';
|
||||
}
|
||||
|
||||
$out .= PHP_EOL;
|
||||
|
||||
foreach ($this->headers as $header => $value) {
|
||||
$out .= $header.': '.$value.PHP_EOL;
|
||||
}
|
||||
|
||||
$out .= PHP_EOL.$this->content.PHP_EOL;
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
78
vendor/discord-php/http/src/Discord/RateLimit.php
vendored
Executable file
78
vendor/discord-php/http/src/Discord/RateLimit.php
vendored
Executable file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Represents a rate-limit given by Discord.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class RateLimit extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Whether the rate-limit is global.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $global;
|
||||
|
||||
/**
|
||||
* Time in seconds of when to retry after.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $retry_after;
|
||||
|
||||
/**
|
||||
* Rate limit constructor.
|
||||
*
|
||||
* @param bool $global
|
||||
* @param float $retry_after
|
||||
*/
|
||||
public function __construct(bool $global, float $retry_after)
|
||||
{
|
||||
$this->global = $global;
|
||||
$this->retry_after = $retry_after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global parameter.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGlobal(): bool
|
||||
{
|
||||
return $this->global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the retry after parameter.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getRetryAfter(): float
|
||||
{
|
||||
return $this->retry_after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a rate-limit to a user-readable string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return 'RATELIMIT '.($this->global ? 'Global' : 'Non-global').', retry after '.$this->retry_after.' s';
|
||||
}
|
||||
}
|
||||
145
vendor/discord-php/http/src/Discord/Request.php
vendored
Executable file
145
vendor/discord-php/http/src/Discord/Request.php
vendored
Executable file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Discord\Http;
|
||||
|
||||
use React\Promise\Deferred;
|
||||
|
||||
/**
|
||||
* Represents an HTTP request.
|
||||
*
|
||||
* @author David Cole <david.cole1340@gmail.com>
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* Deferred promise.
|
||||
*
|
||||
* @var Deferred
|
||||
*/
|
||||
protected $deferred;
|
||||
|
||||
/**
|
||||
* Request method.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* Request URL.
|
||||
*
|
||||
* @var Endpoint
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Request content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Request headers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
*
|
||||
* @param Deferred $deferred
|
||||
* @param string $method
|
||||
* @param Endpoint $url
|
||||
* @param string $content
|
||||
* @param array $headers
|
||||
*/
|
||||
public function __construct(Deferred $deferred, string $method, Endpoint $url, string $content, array $headers = [])
|
||||
{
|
||||
$this->deferred = $deferred;
|
||||
$this->method = $method;
|
||||
$this->url = $url;
|
||||
$this->content = $content;
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string
|
||||
{
|
||||
return Http::BASE_URL.'/'.$this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the headers.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deferred promise.
|
||||
*
|
||||
* @return Deferred
|
||||
*/
|
||||
public function getDeferred(): Deferred
|
||||
{
|
||||
return $this->deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bucket ID for the request.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBucketID(): string
|
||||
{
|
||||
return $this->method.$this->url->toAbsoluteEndpoint(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the request to a user-readable string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return 'REQ '.strtoupper($this->method).' '.$this->url;
|
||||
}
|
||||
}
|
||||
144
vendor/discord-php/http/tests/Discord/DriverInterfaceTest.php
vendored
Executable file
144
vendor/discord-php/http/tests/Discord/DriverInterfaceTest.php
vendored
Executable file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http;
|
||||
|
||||
use Discord\Http\DriverInterface;
|
||||
use Discord\Http\Request;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use function React\Async\await;
|
||||
|
||||
abstract class DriverInterfaceTest extends TestCase
|
||||
{
|
||||
abstract protected function getDriver(): DriverInterface;
|
||||
|
||||
private function getRequest(
|
||||
string $method,
|
||||
string $url,
|
||||
string $content = '',
|
||||
array $headers = []
|
||||
): Request {
|
||||
$request = Mockery::mock(Request::class);
|
||||
|
||||
$request->shouldReceive([
|
||||
'getMethod' => $method,
|
||||
'getUrl' => $url,
|
||||
'getContent' => $content,
|
||||
'getHeaders' => $headers,
|
||||
]);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider requestProvider
|
||||
*/
|
||||
public function testRequest(string $method, string $url, array $content = [], array $verify = [])
|
||||
{
|
||||
$driver = $this->getDriver();
|
||||
$request = $this->getRequest(
|
||||
$method,
|
||||
$url,
|
||||
$content === [] ? '' : json_encode($content),
|
||||
empty($content) ? [] : ['Content-Type' => 'application/json']
|
||||
);
|
||||
|
||||
/** @var ResponseInterface */
|
||||
$response = await($driver->runRequest($request));
|
||||
|
||||
$this->assertNotEquals('', $response->getBody());
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$jsonDecodedBody = json_decode($response->getBody(), true);
|
||||
|
||||
$verify['method'] = $method;
|
||||
|
||||
foreach ($verify as $field => $expectedValue) {
|
||||
$this->assertEquals(
|
||||
$expectedValue,
|
||||
$jsonDecodedBody[$field]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function requestProvider(): array
|
||||
{
|
||||
$content = ['something' => 'value'];
|
||||
|
||||
return [
|
||||
'Plain get' => [
|
||||
'method' => 'GET',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
],
|
||||
'Get with params' => [
|
||||
'method' => 'GET',
|
||||
'url' => 'http://127.0.0.1:8888?something=value',
|
||||
'verify' => [
|
||||
'args' => $content,
|
||||
],
|
||||
],
|
||||
|
||||
'Plain post' => [
|
||||
'method' => 'POST',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
],
|
||||
'Post with content' => [
|
||||
'method' => 'POST',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
'content' => $content,
|
||||
'verify' => [
|
||||
'json' => $content,
|
||||
],
|
||||
],
|
||||
|
||||
'Plain put' => [
|
||||
'method' => 'PUT',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
],
|
||||
'Put with content' => [
|
||||
'method' => 'PUT',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
'content' => $content,
|
||||
'verify' => [
|
||||
'json' => $content,
|
||||
],
|
||||
],
|
||||
|
||||
'Plain patch' => [
|
||||
'method' => 'PATCH',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
],
|
||||
'Patch with content' => [
|
||||
'method' => 'PATCH',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
'content' => $content,
|
||||
'verify' => [
|
||||
'json' => $content,
|
||||
],
|
||||
],
|
||||
|
||||
'Plain delete' => [
|
||||
'method' => 'DELETE',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
],
|
||||
'Delete with content' => [
|
||||
'method' => 'DELETE',
|
||||
'url' => 'http://127.0.0.1:8888',
|
||||
'content' => $content,
|
||||
'verify' => [
|
||||
'json' => $content,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
174
vendor/discord-php/http/tests/Discord/EndpointTest.php
vendored
Executable file
174
vendor/discord-php/http/tests/Discord/EndpointTest.php
vendored
Executable file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http;
|
||||
|
||||
use Discord\Http\Endpoint;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class EndpointTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider majorParamProvider
|
||||
*/
|
||||
public function testBindMajorParams(string $uri, array $replacements, string $expected)
|
||||
{
|
||||
$endpoint = new Endpoint($uri);
|
||||
$endpoint->bindArgs(...$replacements);
|
||||
|
||||
$this->assertEquals(
|
||||
$endpoint->toAbsoluteEndpoint(true),
|
||||
$expected
|
||||
);
|
||||
}
|
||||
|
||||
public function majorParamProvider(): array
|
||||
{
|
||||
return [
|
||||
'Several major params' => [
|
||||
'uri' => 'something/:guild_id/:channel_id/:webhook_id',
|
||||
'replacements' => ['::guild id::', '::channel id::', '::webhook id::'],
|
||||
'expected' => 'something/::guild id::/::channel id::/::webhook id::',
|
||||
],
|
||||
'Single major param' => [
|
||||
'uri' => 'something/:guild_id',
|
||||
'replacements' => ['::guild id::'],
|
||||
'expected' => 'something/::guild id::',
|
||||
],
|
||||
'Single major param, some minor params' => [
|
||||
'uri' => 'something/:guild_id/:some_param/:something_else',
|
||||
'replacements' => ['::guild id::', '::some_param::', '::something else::'],
|
||||
'expected' => 'something/::guild id::/:some_param/:something_else',
|
||||
],
|
||||
'Only minor params' => [
|
||||
'uri' => 'something/:something/:some_param/:something_else',
|
||||
'replacements' => ['::something::', '::some_param::', '::something else::'],
|
||||
'expected' => 'something/:something/:some_param/:something_else',
|
||||
],
|
||||
'Minor and major params in weird order' => [
|
||||
'uri' => 'something/:something/:guild_id/:something_else/:channel_id',
|
||||
'replacements' => ['::something::', '::guild id::', '::something else::', '::channel id::'],
|
||||
'expected' => 'something/:something/::guild id::/:something_else/::channel id::',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider allParamProvider
|
||||
*/
|
||||
public function testBindAllParams(string $uri, array $replacements, string $expected)
|
||||
{
|
||||
$endpoint = new Endpoint($uri);
|
||||
$endpoint->bindArgs(...$replacements);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$endpoint->toAbsoluteEndpoint()
|
||||
);
|
||||
}
|
||||
|
||||
public function allParamProvider(): array
|
||||
{
|
||||
return [
|
||||
'Several major params' => [
|
||||
'uri' => 'something/:guild_id/:channel_id/:webhook_id',
|
||||
'replacements' => ['::guild id::', '::channel id::', '::webhook id::'],
|
||||
'expected' => 'something/::guild id::/::channel id::/::webhook id::',
|
||||
],
|
||||
'Single major param' => [
|
||||
'uri' => 'something/:guild_id',
|
||||
'replacements' => ['::guild id::'],
|
||||
'expected' => 'something/::guild id::',
|
||||
],
|
||||
'Single major param, some minor params' => [
|
||||
'uri' => 'something/:guild_id/:some_param/:something_else',
|
||||
'replacements' => ['::guild id::', '::some param::', '::something else::'],
|
||||
'expected' => 'something/::guild id::/::some param::/::something else::',
|
||||
],
|
||||
'Only minor params' => [
|
||||
'uri' => 'something/:something/:some_param/:other',
|
||||
'replacements' => ['::something::', '::some param::', '::something else::'],
|
||||
'expected' => 'something/::something::/::some param::/::something else::',
|
||||
],
|
||||
'Minor and major params in weird order' => [
|
||||
'uri' => 'something/:something/:guild_id/:other/:channel_id',
|
||||
'replacements' => ['::something::', '::guild id::', '::something else::', '::channel id::'],
|
||||
'expected' => 'something/::something::/::guild id::/::something else::/::channel id::',
|
||||
],
|
||||
|
||||
// @see https://github.com/discord-php/DiscordPHP-Http/issues/16
|
||||
// 'Params with same prefix, short first' => [
|
||||
// 'uri' => 'something/:thing/:thing_other',
|
||||
// 'replacements' => ['::thing::', '::thing other::'],
|
||||
// 'expected' => 'something/::thing::/::thing other::',
|
||||
// ],
|
||||
// 'Params with same prefix, short first' => [
|
||||
// 'uri' => 'something/:thing_other/:thing',
|
||||
// 'replacements' => ['::thing other::', '::thing::'],
|
||||
// 'expected' => 'something/::thing other::/::thing::',
|
||||
// ],
|
||||
];
|
||||
}
|
||||
|
||||
public function testBindAssoc()
|
||||
{
|
||||
$endpoint = new Endpoint('something/:first/:second');
|
||||
$endpoint->bindAssoc([
|
||||
'second' => '::second::',
|
||||
'first' => '::first::',
|
||||
]);
|
||||
|
||||
$this->assertEquals(
|
||||
'something/::first::/::second::',
|
||||
$endpoint->toAbsoluteEndpoint()
|
||||
);
|
||||
}
|
||||
|
||||
public function testItConvertsToString()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'something/::first::/::second::',
|
||||
(string) Endpoint::bind(
|
||||
'something/:first/:second',
|
||||
'::first::',
|
||||
'::second::'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function itCanAddQueryParams()
|
||||
{
|
||||
$endpoint = new Endpoint('something/:param');
|
||||
$endpoint->bindArgs('param');
|
||||
|
||||
$endpoint->addQuery('something', 'value');
|
||||
$endpoint->addQuery('boolval', true);
|
||||
|
||||
$this->assertEquals(
|
||||
'something/param?something=value&boolval=1',
|
||||
$endpoint->toAbsoluteEndpoint()
|
||||
);
|
||||
}
|
||||
|
||||
public function itDoesNotAddQueryParamsForMajorParameters()
|
||||
{
|
||||
$endpoint = new Endpoint('something/:guild_id');
|
||||
$endpoint->bindArgs('param');
|
||||
|
||||
$endpoint->addQuery('something', 'value');
|
||||
$endpoint->addQuery('boolval', true);
|
||||
|
||||
$this->assertEquals(
|
||||
'something/param',
|
||||
$endpoint->toAbsoluteEndpoint(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
126
vendor/discord-php/http/tests/Discord/Multipart/MultipartTest.php
vendored
Executable file
126
vendor/discord-php/http/tests/Discord/Multipart/MultipartTest.php
vendored
Executable file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http\Multipart;
|
||||
|
||||
use Discord\Http\Multipart\MultipartBody;
|
||||
use Discord\Http\Multipart\MultipartField;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MultipartTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider multipartFieldStringConversionProvider
|
||||
*/
|
||||
public function testMultipartFieldStringConversion(array $constructorArgs, string $expected)
|
||||
{
|
||||
$multipartField = new MultipartField(...$constructorArgs);
|
||||
|
||||
$this->assertEquals($expected, (string) $multipartField);
|
||||
}
|
||||
|
||||
public function multipartFieldStringConversionProvider(): array
|
||||
{
|
||||
return [
|
||||
'Completely filled' => [
|
||||
'args' => [
|
||||
'::name::',
|
||||
'::content::',
|
||||
[
|
||||
'Header-Name' => 'Value',
|
||||
],
|
||||
'::filename::',
|
||||
],
|
||||
|
||||
'expected' => <<<EXPECTED
|
||||
Content-Disposition: form-data; name="::name::"; filename="%3A%3Afilename%3A%3A"
|
||||
Header-Name: Value
|
||||
|
||||
::content::
|
||||
|
||||
EXPECTED
|
||||
],
|
||||
'Missing filename' => [
|
||||
'args' => [
|
||||
'::name::',
|
||||
'::content::',
|
||||
[
|
||||
'Header-Name' => 'Value',
|
||||
],
|
||||
null,
|
||||
],
|
||||
|
||||
'expected' => <<<EXPECTED
|
||||
Content-Disposition: form-data; name="::name::"
|
||||
Header-Name: Value
|
||||
|
||||
::content::
|
||||
|
||||
EXPECTED
|
||||
],
|
||||
'No headers' => [
|
||||
'args' => [
|
||||
'::name::',
|
||||
'::content::',
|
||||
[],
|
||||
'::filename::',
|
||||
],
|
||||
|
||||
'expected' => <<<EXPECTED
|
||||
Content-Disposition: form-data; name="::name::"; filename="%3A%3Afilename%3A%3A"
|
||||
|
||||
::content::
|
||||
|
||||
EXPECTED
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testMultipartBodyBuilding()
|
||||
{
|
||||
$fields = array_map(function (string $return) {
|
||||
$mock = Mockery::mock(MultipartField::class);
|
||||
$mock->shouldReceive('__toString')->andReturn($return);
|
||||
|
||||
return $mock;
|
||||
}, ['::first field::', '::second field::', '::third field::']);
|
||||
|
||||
$multipartBody = new MultipartBody($fields, '::boundary::');
|
||||
|
||||
$this->assertEquals(
|
||||
<<<EXPECTED
|
||||
--::boundary::
|
||||
::first field::
|
||||
--::boundary::
|
||||
::second field::
|
||||
--::boundary::
|
||||
::third field::
|
||||
--::boundary::--
|
||||
EXPECTED,
|
||||
(string) $multipartBody
|
||||
);
|
||||
|
||||
$this->assertEquals([
|
||||
'Content-Type' => 'multipart/form-data; boundary=::boundary::',
|
||||
'Content-Length' => strlen((string) $multipartBody),
|
||||
], $multipartBody->getHeaders());
|
||||
}
|
||||
|
||||
public function testGeneratingBoundary()
|
||||
{
|
||||
$multipartBody = new MultipartBody([
|
||||
Mockery::mock(MultipartField::class),
|
||||
]);
|
||||
|
||||
$this->assertNotNull($multipartBody->boundary);
|
||||
}
|
||||
}
|
||||
87
vendor/discord-php/http/tests/Discord/RequestTest.php
vendored
Executable file
87
vendor/discord-php/http/tests/Discord/RequestTest.php
vendored
Executable file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http;
|
||||
|
||||
use Discord\Http\Endpoint;
|
||||
use Discord\Http\Http;
|
||||
use Discord\Http\Request;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
class RequestTest extends TestCase
|
||||
{
|
||||
private function getRequest(
|
||||
?Deferred $deferred = null,
|
||||
string $method = '',
|
||||
?Endpoint $url = null,
|
||||
string $content = '',
|
||||
array $headers = []
|
||||
) {
|
||||
$url = $url ?? new Endpoint('');
|
||||
$deferred = $deferred ?? new Deferred();
|
||||
|
||||
return new Request(
|
||||
$deferred,
|
||||
$method,
|
||||
$url,
|
||||
$content,
|
||||
$headers
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetDeferred()
|
||||
{
|
||||
$deferred = Mockery::mock(Deferred::class);
|
||||
$request = $this->getRequest($deferred);
|
||||
|
||||
$this->assertEquals($deferred, $request->getDeferred());
|
||||
}
|
||||
|
||||
public function testGetMethod()
|
||||
{
|
||||
$request = $this->getRequest(null, '::method::');
|
||||
|
||||
$this->assertEquals('::method::', $request->getMethod());
|
||||
}
|
||||
|
||||
public function testGetUrl()
|
||||
{
|
||||
$request = $this->getRequest(null, '', new Endpoint('::url::'));
|
||||
|
||||
$this->assertEquals(Http::BASE_URL.'/::url::', $request->getUrl());
|
||||
}
|
||||
|
||||
public function testGetContent()
|
||||
{
|
||||
$request = $this->getRequest(null, '', null, '::content::');
|
||||
|
||||
$this->assertEquals('::content::', $request->getContent());
|
||||
}
|
||||
|
||||
public function testGetHeaders()
|
||||
{
|
||||
$request = $this->getRequest(null, '', null, '::content::', ['something' => 'value']);
|
||||
|
||||
$this->assertEquals(['something' => 'value'], $request->getHeaders());
|
||||
}
|
||||
|
||||
public function testGetBucketId()
|
||||
{
|
||||
$endpoint = Mockery::mock(Endpoint::class);
|
||||
$endpoint->shouldReceive('toAbsoluteEndpoint')->andReturn('::endpoint::');
|
||||
|
||||
$request = $this->getRequest(null, '::method::', $endpoint);
|
||||
|
||||
$this->assertEquals('::method::::endpoint::', $request->getBucketID());
|
||||
}
|
||||
}
|
||||
25
vendor/discord-php/http/tests/Drivers/GuzzleTest.php
vendored
Executable file
25
vendor/discord-php/http/tests/Drivers/GuzzleTest.php
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http\Drivers;
|
||||
|
||||
use Discord\Http\DriverInterface;
|
||||
use Discord\Http\Drivers\Guzzle;
|
||||
use React\EventLoop\Loop;
|
||||
use Tests\Discord\Http\DriverInterfaceTest;
|
||||
|
||||
class GuzzleTest extends DriverInterfaceTest
|
||||
{
|
||||
protected function getDriver(): DriverInterface
|
||||
{
|
||||
return new Guzzle(Loop::get());
|
||||
}
|
||||
}
|
||||
25
vendor/discord-php/http/tests/Drivers/ReactTest.php
vendored
Executable file
25
vendor/discord-php/http/tests/Drivers/ReactTest.php
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
namespace Tests\Discord\Http\Drivers;
|
||||
|
||||
use Discord\Http\DriverInterface;
|
||||
use Discord\Http\Drivers\React;
|
||||
use React\EventLoop\Loop;
|
||||
use Tests\Discord\Http\DriverInterfaceTest;
|
||||
|
||||
class ReactTest extends DriverInterfaceTest
|
||||
{
|
||||
protected function getDriver(): DriverInterface
|
||||
{
|
||||
return new React(Loop::get());
|
||||
}
|
||||
}
|
||||
32
vendor/discord-php/http/tests/Drivers/_server.php
vendored
Executable file
32
vendor/discord-php/http/tests/Drivers/_server.php
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP-Http project.
|
||||
*
|
||||
* Copyright (c) 2021-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE file.
|
||||
*/
|
||||
|
||||
use React\Http\HttpServer;
|
||||
use React\Http\Message\Response;
|
||||
use React\Socket\SocketServer;
|
||||
|
||||
require __DIR__.'/../../vendor/autoload.php';
|
||||
|
||||
$http = new HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
|
||||
$response = [
|
||||
'method' => $request->getMethod(),
|
||||
'args' => $request->getQueryParams(),
|
||||
'json' => $request->getHeader('Content-Type') === ['application/json']
|
||||
? json_decode($request->getBody())
|
||||
: [],
|
||||
];
|
||||
|
||||
return Response::json($response);
|
||||
});
|
||||
|
||||
$socket = new SocketServer('127.0.0.1:8888');
|
||||
|
||||
$http->listen($socket);
|
||||
Reference in New Issue
Block a user