Compare commits

..

12 Commits

Author SHA1 Message Date
Alex 8419a7892d Merge remote-tracking branch 'origin/master' 2025-09-29 19:28:41 +03:00
Alex ddc57d7ab8 Change api domain and authorization with header 2025-09-29 19:28:20 +03:00
Alex d0f044c7bd Change api domain and authorization with header 2025-09-29 19:25:28 +03:00
Alex 31f109db7e Merge pull request #8 from BushlanovDev/docs
Update documentation
2025-09-16 21:25:38 +03:00
Alex e1375acabb Update documentation 2025-09-16 21:22:04 +03:00
Alex 098aa44825 Update documentation 2025-09-15 23:04:15 +03:00
Alex 6137b2ea6e Merge pull request #7 from BushlanovDev/docs
Update documentation
2025-09-14 15:55:33 +03:00
Alex 122afcc7c0 Merge branch 'master' into docs 2025-09-14 15:44:19 +03:00
Alex 4d56119fa2 Disable deprecation error for unit test 2025-09-13 19:53:43 +03:00
Alex ae53f51854 Disable deprecation error for unit test 2025-09-13 19:26:13 +03:00
Alex b87b41ff8f Update documentation 2025-09-12 22:06:23 +03:00
Alex 493be1a9a0 Add php8.5 to github action 2025-09-10 21:25:52 +03:00
8 changed files with 248 additions and 58 deletions
+7 -13
View File
@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ "8.3", "8.4" ]
php-versions: [ "8.3", "8.4", "8.5" ]
name: PHP ${{ matrix.php-versions }} Test on ubuntu-latest
steps:
@@ -43,19 +43,13 @@ jobs:
php-version: ${{ matrix.php-versions }}
coverage: xdebug
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install composer dependencies
run: composer install --prefer-dist --no-interaction
run: |
if [[ "${{ matrix.php-versions }}" == "8.5" ]]; then
composer install --prefer-dist --no-interaction --ignore-platform-req=php
else
composer install --prefer-dist --no-interaction
fi
- name: PHPUnit
run: ./vendor/bin/phpunit --configuration=phpunit.xml --coverage-text
+201 -31
View File
@@ -1,7 +1,6 @@
- [Быстрый старт](#Быстрый-старт)
- [Получение токена](#Получение-токена)
- [Установка библиотеки](#Установка-библиотеки)
- [Установка библиотеки в Laravel](#Установка-библиотеки-в-Laravel)
- [Инициализация бота](#Инициализация-бота)
- [Информация о боте](#Информация-о-боте)
- `GET /me` (`getBotInfo`) - [*Получение информации о боте.*](#Получение-информации-о-боте)
@@ -40,6 +39,18 @@
- `GET /messages/{messageId}` (`getMessageById`) - [*Получение сообщения по ID.*](#Получение-сообщения-по-ID)
- `GET /videos/{videoToken}` (`getVideoAttachmentDetails`) - [*Получение детальной информации о видео.*](#Получение-детальной-информации-о-видео)
- `POST /answers` (`answerOnCallback`) - [*Ответ на нажатие callback-кнопки.*](#Ответ-на-нажатие-callback-кнопки)
- [Laravel](#Laravel)
- [Регистрация пакета](#Регистрация-пакета)
- [Настройка](#Настройка)
- [Использование](#Использование)
- [Artisan команды](#Artisan-команды)
- [Подписка на Webhook](#Подписка-на-Webhook)
- [Удаление подписки](#Удаление-подписки)
- [Список подписок](#Список-подписок)
- [Обработка хуков](#Обработка-хуков)
- [Long Polling](#Long-Polling)
- [Handler Classes](#Handler-Classes)
- [Тестирование](#Тестирование)
## Быстрый старт
@@ -56,36 +67,6 @@
composer require bushlanov-dev/max-bot-api-client-php
```
### Установка библиотеки в Laravel
Пользователи Laravel могут зарегистрировать сервис провайдер и фасад в `config/app.php`:
```php
'providers' => [
// ...
BushlanovDev\MaxMessengerBot\Laravel\MaxBotServiceProvider::class,
],
// ...
'aliases' => [
// ...
'MaxBot' => BushlanovDev\MaxMessengerBot\Laravel\MaxBotFacade::class,
],
```
При не необходимости опубликовать конфиг выполните следующую команду
```bash
php artisan vendor:publish --provider="BushlanovDev\MaxMessengerBot\Laravel\MaxBotServiceProvider"
```
Для работы вам потребуется внести следующие настройки в `.env`
```env
MAXBOT_ACCESS_TOKEN=your_bot_access_token_here
MAXBOT_WEBHOOK_SECRET=your_webhook_secret_here
MAXBOT_LOGGING_ENABLED=true
```
### Инициализация бота
Единственной обязательной настройкой является токен вашего бота.
@@ -444,3 +425,192 @@ $api->answerOnCallback(
notify: true, // Заполните это, если хотите просто отправить одноразовое уведомление пользователю (необязательно)
);
```
## Laravel
### Регистрация пакета
Пакет будет автоматически обнаружен Laravel.
Если автоматическое обнаружение отключено можно зарегистрировать сервис провайдер и фасад в `config/app.php`:
```php
'providers' => [
// ...
BushlanovDev\MaxMessengerBot\Laravel\MaxBotServiceProvider::class,
],
// ...
'aliases' => [
// ...
'MaxBot' => BushlanovDev\MaxMessengerBot\Laravel\MaxBotFacade::class,
],
```
### Настройка
При не необходимости опубликовать конфиг выполните следующую команду:
```bash
php artisan vendor:publish --provider="BushlanovDev\MaxMessengerBot\Laravel\MaxBotServiceProvider"
```
Для работы вам потребуется добавить ваш токен в файл `.env`
```env
MAXBOT_ACCESS_TOKEN=your_bot_access_token_here
MAXBOT_WEBHOOK_SECRET=your_webhook_secret_here
```
### Использование
Все методы бота доступны через фасад MaxBot, например:
```php
use MaxBot;
// Отправка сообщения пользователю
MaxBot::sendUserMessage(123456, 'Hello from Laravel!');
// Получение обновления
$updates = MaxBot::getUpdates();
```
### Artisan команды
#### Подписка на Webhook
```bash
# Подписка на получение обновлений
php artisan maxbot:webhook:subscribe https://yourapp.com/bot/webhook
# С верификацией
php artisan maxbot:webhook:subscribe https://yourapp.com/bot/webhook --secret=your_secret_key
# Подписка только на определенные типы событий
php artisan maxbot:webhook:subscribe https://yourapp.com/bot/webhook --types=message_created --types=message_callback
```
#### Удаление подписки
```bash
php artisan maxbot:webhook:unsubscribe https://yourapp.com/bot/webhook
```
#### Список подписок
```bash
php artisan maxbot:webhook:list
```
### Обработка хуков
Создайте контроллер для обработчика:
```php
use Illuminate\Http\Request;
use BushlanovDev\MaxMessengerBot\Laravel\MaxBotManager;
class WebhookController extends Controller
{
public function handle(Request $request, MaxBotManager $botManager)
{
// Обработчик сообщений
$botManager->onMessageCreated(function (MessageCreatedUpdate $update) {
$message = $update->message;
// ...
});
// Обработчик команды /start
$botManager->onCommand('start', function (MessageCreatedUpdate $update) {
// ...
});
// Using Laravel container bindings
$botManager->onMessageCreated(MessageHandler::class);
$botManager->onCommand('help', HelpCommandHandler::class);
return $botManager->handleWebhook($request);
}
}
```
Добавьте маршрут в `routes/web.php`:
```php
Route::post('/bot/webhook', [WebhookController::class, 'handle']);
```
### Long Polling
Создайте artisan команду для получения long polling обновлений:
```php
use Illuminate\Console\Command;
use BushlanovDev\MaxMessengerBot\Laravel\MaxBotManager;
class BotPollingCommand extends Command
{
protected $signature = 'bot:polling';
protected $description = 'Start bot long polling';
public function handle(MaxBotManager $botManager)
{
$this->info('Starting bot polling...');
$botManager->startLongPolling();
}
}
```
### Handler Classes
Примеры классов обработчиков обновлений:
```php
class MessageHandler
{
public function handle(MessageCreatedUpdate $update)
{
$message = $update->message;
$text = $message->body?->text;
if ($text) {
app(Api::class)->sendMessage(
userId: $message->sender->userId,
text: "You said: $text",
);
}
}
}
class HelpCommandHandler
{
public function handle(MessageCreatedUpdate $update)
{
app(Api::class)->sendMessage(
userId: $update->message->sender->userId,
text: "This is help message",
);
}
}
```
### Тестирование
Вы можете использовать мок API для тестирования:
```php
use BushlanovDev\MaxMessengerBot\Api;
class BotTest extends TestCase
{
public function test_bot_sends_message()
{
$apiMock = $this->createMock(Api::class);
$apiMock->expects($this->once())
->method('sendUserMessage')
->with(123456, 'Hello!');
$this->app->instance(Api::class, $apiMock);
// Your test code here
}
}
```
+13 -3
View File
@@ -1,14 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
bootstrap="tests/bootstrap.php"
testdox="false"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
displayDetailsOnPhpunitDeprecations="true"
failOnPhpunitDeprecation="true"
failOnDeprecation="false"
failOnPhpunitDeprecation="false"
displayDetailsOnPhpunitDeprecations="false"
displayDetailsOnTestsThatTriggerDeprecations="false"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true"
failOnRisky="true"
failOnWarning="true">
<testsuites>
@@ -25,4 +31,8 @@
<directory suffix=".php">src/Exceptions</directory>
</exclude>
</source>
<php>
<ini name="error_reporting" value="E_ALL &amp; ~E_DEPRECATED"/>
</php>
</phpunit>
+2 -2
View File
@@ -48,11 +48,11 @@ use RuntimeException;
*/
class Api
{
public const string LIBRARY_VERSION = '1.2.1';
public const string LIBRARY_VERSION = '1.3.0';
public const string API_VERSION = '0.0.6';
private const string API_BASE_URL = 'https://botapi.max.ru';
private const string API_BASE_URL = 'https://platform-api.max.ru';
private const string METHOD_GET = 'GET';
private const string METHOD_POST = 'POST';
+2 -2
View File
@@ -60,7 +60,6 @@ final readonly class Client implements ClientApiInterface
*/
public function request(string $method, string $uri, array $queryParams = [], array $body = []): array
{
$queryParams['access_token'] = $this->accessToken;
if (!empty($this->apiVersion)) {
$queryParams['v'] = $this->apiVersion;
}
@@ -83,7 +82,8 @@ final readonly class Client implements ClientApiInterface
$stream = $this->streamFactory->createStream($payload);
$request = $request
->withBody($stream)
->withHeader('Content-Type', 'application/json; charset=utf-8');
->withHeader('Content-Type', 'application/json; charset=utf-8')
->withHeader('Authorization', $this->accessToken);
}
try {
+16 -7
View File
@@ -35,7 +35,7 @@ final class ClientTest extends TestCase
{
private const string FAKE_TOKEN = '12345:abcdef';
private const string API_VERSION = '0.0.6';
private const string API_BASE_URL = 'https://botapi.max.ru';
private const string API_BASE_URL = 'https://platform-api.max.ru';
private MockObject&ClientInterface $httpClientMock;
private MockObject&RequestFactoryInterface $requestFactoryMock;
@@ -96,7 +96,6 @@ final class ClientTest extends TestCase
{
$uri = '/me';
$expectedUrl = self::API_BASE_URL . $uri . '?' . http_build_query([
'access_token' => self::FAKE_TOKEN,
'v' => self::API_VERSION,
]);
$responsePayload = ['id' => 987, 'name' => 'TestBot'];
@@ -138,8 +137,7 @@ final class ClientTest extends TestCase
];
$responsePayload = ['success' => true];
$expectedUrl = self::API_BASE_URL . $uri . '?' . http_build_query([
'access_token' => self::FAKE_TOKEN,
'v' => self::API_VERSION
'v' => self::API_VERSION,
]);
$this->requestFactoryMock
@@ -157,12 +155,23 @@ final class ClientTest extends TestCase
}))
->willReturn($this->requestMock);
$headerCallCount = 0;
$this->requestMock
->expects($this->once())
->expects($this->exactly(2))
->method('withHeader')
->with('Content-Type', 'application/json; charset=utf-8')
->willReturn($this->requestMock);
->willReturnCallback(function (string $header, string $value) use (&$headerCallCount) {
if ($headerCallCount === 0) {
$this->assertSame('Content-Type', $header);
$this->assertSame('application/json; charset=utf-8', $value);
} elseif ($headerCallCount === 1) {
$this->assertSame('Authorization', $header);
$this->assertSame(self::FAKE_TOKEN, $value);
}
$headerCallCount++;
return $this->requestMock;
});
$this->responseMock->method('getStatusCode')->willReturn(200);
$this->streamMock->method('__toString')->willReturn(json_encode($responsePayload));
@@ -92,6 +92,8 @@ final class MaxBotServiceProviderTest extends TestCase
#[PreserveGlobalState(false)]
public function itThrowsExceptionWhenGuzzleIsMissing(): void
{
error_reporting(E_ALL & ~E_DEPRECATED);
$this->expectException(LogicException::class);
$this->expectExceptionMessage(
'Guzzle HTTP client is required. Please run "composer require guzzlehttp/guzzle".'
+5
View File
@@ -0,0 +1,5 @@
<?php
error_reporting(E_ALL & ~E_DEPRECATED);
require_once dirname(__DIR__) . '/vendor/autoload.php';