mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-24 12:36:50 +00:00
Added PSR LoggerInterface
This commit is contained in:
+19
-4
@@ -37,6 +37,8 @@ use BushlanovDev\MaxMessengerBot\Models\VideoAttachmentDetails;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use ReflectionException;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -50,7 +52,7 @@ class Api
|
||||
{
|
||||
public const string API_VERSION = '0.0.6';
|
||||
|
||||
private const string API_BASE_URL = 'https://botapi.max.ru';
|
||||
private const string API_BASE_URL = 'http://127.0.0.1:5001';
|
||||
|
||||
private const string METHOD_GET = 'GET';
|
||||
private const string METHOD_POST = 'POST';
|
||||
@@ -77,12 +79,15 @@ class Api
|
||||
|
||||
private readonly ModelFactory $modelFactory;
|
||||
|
||||
private readonly LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* Api constructor.
|
||||
*
|
||||
* @param string $accessToken Your bot's access token from @MasterBot.
|
||||
* @param ClientApiInterface|null $client Http api client.
|
||||
* @param ModelFactory|null $modelFactory
|
||||
* @param LoggerInterface|null $logger
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
@@ -90,7 +95,10 @@ class Api
|
||||
string $accessToken,
|
||||
?ClientApiInterface $client = null,
|
||||
?ModelFactory $modelFactory = null,
|
||||
?LoggerInterface $logger = null,
|
||||
) {
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
|
||||
if ($client === null) {
|
||||
if (!class_exists(\GuzzleHttp\Client::class) || !class_exists(\GuzzleHttp\Psr7\HttpFactory::class)) {
|
||||
throw new LogicException(
|
||||
@@ -108,6 +116,7 @@ class Api
|
||||
$httpFactory,
|
||||
self::API_BASE_URL,
|
||||
self::API_VERSION,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,7 +153,7 @@ class Api
|
||||
*/
|
||||
public function createWebhookHandler(?string $secret = null): WebhookHandler
|
||||
{
|
||||
return new WebhookHandler($this, $this->modelFactory, $secret);
|
||||
return new WebhookHandler($this, $this->modelFactory, $secret, $this->logger);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,10 +260,16 @@ class Api
|
||||
try {
|
||||
$this->processUpdatesBatch($handlers, $timeout, $marker);
|
||||
} catch (NetworkException $e) {
|
||||
error_log('Network error: ' . $e->getMessage());
|
||||
$this->logger->error(
|
||||
'Long-polling network error: {message}',
|
||||
['message' => $e->getMessage(), 'exception' => $e],
|
||||
);
|
||||
sleep(5);
|
||||
} catch (\Exception $e) {
|
||||
error_log('An error occurred: ' . $e->getMessage());
|
||||
$this->logger->error(
|
||||
'An error occurred during long-polling: {message}',
|
||||
['message' => $e->getMessage(), 'exception' => $e],
|
||||
);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* The low-level HTTP client responsible for communicating with the Max Bot API.
|
||||
@@ -34,6 +36,7 @@ final readonly class Client implements ClientApiInterface
|
||||
* @param StreamFactoryInterface $streamFactory A PSR-17 factory for creating request body streams.
|
||||
* @param string $baseUrl The base URL for API requests.
|
||||
* @param string|null $apiVersion The API version to use for requests.
|
||||
* @param LoggerInterface $logger
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
@@ -44,6 +47,7 @@ final readonly class Client implements ClientApiInterface
|
||||
private StreamFactoryInterface $streamFactory,
|
||||
private string $baseUrl,
|
||||
private ?string $apiVersion = null,
|
||||
private LoggerInterface $logger = new NullLogger(),
|
||||
) {
|
||||
if (empty($accessToken)) {
|
||||
throw new InvalidArgumentException('Access token cannot be empty.');
|
||||
@@ -60,6 +64,12 @@ final readonly class Client implements ClientApiInterface
|
||||
$queryParams['v'] = $this->apiVersion;
|
||||
}
|
||||
|
||||
$this->logger->debug('Sending API request', [
|
||||
'method' => $method,
|
||||
'url' => $this->baseUrl . $uri,
|
||||
'body' => $body,
|
||||
]);
|
||||
|
||||
$fullUrl = $this->baseUrl . $uri . '?' . http_build_query($queryParams);
|
||||
$request = $this->requestFactory->createRequest($method, $fullUrl);
|
||||
|
||||
@@ -79,6 +89,10 @@ final readonly class Client implements ClientApiInterface
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
} catch (ClientExceptionInterface $e) {
|
||||
// This catches network errors, DNS failures, timeouts, etc.
|
||||
$this->logger->error('Network exception during API request', [
|
||||
'message' => $e->getMessage(),
|
||||
'exception' => $e,
|
||||
]);
|
||||
throw new NetworkException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
@@ -86,6 +100,11 @@ final readonly class Client implements ClientApiInterface
|
||||
|
||||
$responseBody = (string)$response->getBody();
|
||||
|
||||
$this->logger->debug('Received API response', [
|
||||
'status' => $response->getStatusCode(),
|
||||
'body' => $responseBody,
|
||||
]);
|
||||
|
||||
// Handle successful but empty responses (e.g., from DELETE endpoints)
|
||||
if (empty($responseBody)) {
|
||||
// The API spec often returns {"success": true}, so we can simulate that
|
||||
@@ -161,6 +180,11 @@ final readonly class Client implements ClientApiInterface
|
||||
$errorCode = $data['code'] ?? 'unknown';
|
||||
$errorMessage = $data['message'] ?? 'An unknown error occurred.';
|
||||
|
||||
$this->logger->error('API error response received', [
|
||||
'status' => $statusCode,
|
||||
'body' => $responseBody,
|
||||
]);
|
||||
|
||||
throw match ($statusCode) {
|
||||
401 => new UnauthorizedException($errorMessage, $errorCode, $response),
|
||||
403 => new ForbiddenException($errorMessage, $errorCode, $response),
|
||||
|
||||
@@ -9,6 +9,8 @@ use BushlanovDev\MaxMessengerBot\Exceptions\SecurityException;
|
||||
use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\AbstractUpdate;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* A class designed to process incoming webhook requests from the Max API.
|
||||
@@ -26,11 +28,13 @@ final class WebhookHandler
|
||||
* @param Api $api An instance of the Api to be passed to handlers for immediate responses.
|
||||
* @param ModelFactory $modelFactory An instance of the model factory to create Update objects.
|
||||
* @param string|null $secret The secret key provided during webhook subscription to verify requests.
|
||||
* @param LoggerInterface $logger A PSR-3 compatible logger.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly Api $api,
|
||||
private readonly ModelFactory $modelFactory,
|
||||
private readonly ?string $secret = null,
|
||||
private readonly LoggerInterface $logger = new NullLogger(),
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -254,6 +258,8 @@ final class WebhookHandler
|
||||
$payload = (string)$request->getBody();
|
||||
$signature = $request->getHeaderLine('X-Max-Bot-Api-Secret');
|
||||
|
||||
$this->logger->debug('Received webhook payload', ['body' => $payload]);
|
||||
|
||||
if (empty($payload)) {
|
||||
throw new SerializationException('Webhook body is empty.');
|
||||
}
|
||||
@@ -263,6 +269,7 @@ final class WebhookHandler
|
||||
try {
|
||||
$data = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
$this->logger->error('Failed to decode webhook JSON', ['payload' => $payload, 'exception' => $e]);
|
||||
throw new SerializationException('Failed to decode webhook body as JSON.', 0, $e);
|
||||
}
|
||||
|
||||
@@ -297,6 +304,7 @@ final class WebhookHandler
|
||||
}
|
||||
|
||||
if (!hash_equals($this->secret, $signature)) {
|
||||
$this->logger->warning('Webhook signature verification failed', ['received_signature' => $signature]);
|
||||
throw new SecurityException('Signature verification failed.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user