Skip to content

Commit 3b242c1

Browse files
committed
Laravel support
1 parent 59a8436 commit 3b242c1

10 files changed

+1022
-4
lines changed

composer.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bushlanov-dev/max-bot-api-client-php",
33
"description": "Max Bot API Client library",
4-
"keywords": ["max messenger", "bot", "max", "api"],
4+
"keywords": ["max messenger", "bot", "max", "api", "max bot", "laravel", "laravel max bot"],
55
"type": "library",
66
"license": "MIT",
77
"authors": [
@@ -17,15 +17,19 @@
1717
"ext-json": "*",
1818
"guzzlehttp/guzzle": "^6.5.8||^7.0",
1919
"guzzlehttp/psr7": "^1.8||^2.0",
20-
"psr/log": "^3.0",
2120
"psr/http-client": "^1.0",
2221
"psr/http-factory": "^1.0",
23-
"psr/http-message": "^1.0||^2.0"
22+
"psr/http-message": "^1.0||^2.0",
23+
"psr/log": "^3.0"
2424
},
2525
"require-dev": {
2626
"friendsofphp/php-cs-fixer": "^3.77",
27+
"illuminate/console": "^12.22",
28+
"illuminate/http": "^12.22",
29+
"illuminate/testing": "^11.0||^12.0",
2730
"jaschilz/php-coverage-badger": "^2.0",
2831
"mikey179/vfsstream": "^1.6",
32+
"mockery/mockery": "^1.6",
2933
"php-mock/php-mock-phpunit": "^2.13",
3034
"phpstan/phpstan": "^2.1",
3135
"phpunit/phpunit": "^12.0",
@@ -44,6 +48,16 @@
4448
"config": {
4549
"sort-packages": true
4650
},
51+
"extra": {
52+
"laravel": {
53+
"providers": [
54+
"BushlanovDev\\MaxMessengerBot\\Laravel\\MaxBotServiceProvider"
55+
],
56+
"aliases": {
57+
"MaxBot": "BushlanovDev\\MaxMessengerBot\\Laravel\\MaxBotFacade"
58+
}
59+
}
60+
},
4761
"scripts": {
4862
"analyse": "vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=256M",
4963
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes src",

src/Api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ public function getMembers(
843843
int $chatId,
844844
?array $userIds = null,
845845
?int $marker = null,
846-
?int $count = null
846+
?int $count = null,
847847
): ChatMembersList {
848848
$query = [
849849
'user_ids' => $userIds !== null ? implode(',', $userIds) : null,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BushlanovDev\MaxMessengerBot\Laravel\Commands;
6+
7+
use BushlanovDev\MaxMessengerBot\Laravel\MaxBotManager;
8+
use Illuminate\Console\Command;
9+
use Illuminate\Support\Facades\Log;
10+
use Throwable;
11+
12+
/**
13+
* Artisan command to start processing updates via long polling.
14+
*/
15+
class PollingStartCommand extends Command
16+
{
17+
/**
18+
* The name and signature of the console command.
19+
*
20+
* @var string
21+
*/
22+
protected $signature = 'maxbot:polling:start
23+
{--timeout=90 : Timeout in seconds for long polling}';
24+
25+
/**
26+
* The console command description.
27+
*
28+
* @var string
29+
*/
30+
protected $description = 'Start the bot to process updates via long polling';
31+
32+
/**
33+
* Execute the console command.
34+
*/
35+
public function handle(MaxBotManager $botManager): int
36+
{
37+
$timeout = (int)$this->option('timeout');
38+
39+
$this->info("Starting long polling with a timeout of $timeout seconds... Press Ctrl+C to stop.");
40+
41+
try {
42+
$botManager->startLongPolling($timeout);
43+
44+
// @codeCoverageIgnoreStart
45+
// This part is unreachable as startLongPolling is an infinite loop
46+
return self::SUCCESS;
47+
// @codeCoverageIgnoreEnd
48+
} catch (Throwable $e) {
49+
Log::error("Long polling failed to start or crashed: {$e->getMessage()}", [
50+
'exception' => $e,
51+
]);
52+
$this->error("❌ Long polling failed: {$e->getMessage()}");
53+
54+
return self::FAILURE;
55+
}
56+
}
57+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BushlanovDev\MaxMessengerBot\Laravel\Commands;
6+
7+
use BushlanovDev\MaxMessengerBot\Api;
8+
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
9+
use Illuminate\Console\Command;
10+
use Illuminate\Support\Facades\Log;
11+
use Throwable;
12+
13+
/**
14+
* Artisan command for listing active webhook subscriptions.
15+
*/
16+
class WebhookListCommand extends Command
17+
{
18+
/**
19+
* The name and signature of the console command.
20+
*
21+
* @var string
22+
*/
23+
protected $signature = 'maxbot:webhook:list';
24+
25+
/**
26+
* The console command description.
27+
*
28+
* @var string
29+
*/
30+
protected $description = 'List all active webhook subscriptions';
31+
32+
/**
33+
* Execute the console command.
34+
*/
35+
public function handle(Api $api): int
36+
{
37+
$this->info('Fetching webhook subscriptions...');
38+
39+
try {
40+
$subscriptions = $api->getSubscriptions();
41+
42+
if (empty($subscriptions)) {
43+
$this->info('No active webhook subscriptions found.');
44+
45+
return self::SUCCESS;
46+
}
47+
48+
$this->info('Found ' . count($subscriptions) . ' active webhook subscription(s):');
49+
$this->newLine();
50+
51+
$headers = ['URL', 'Update Types', 'Created At'];
52+
$rows = [];
53+
54+
foreach ($subscriptions as $subscription) {
55+
$rows[] = [
56+
$subscription->url,
57+
implode(', ', $subscription->updateTypes ? array_map(fn (UpdateType $updateType) => $updateType->value, $subscription->updateTypes) : ['all']),
58+
date('Y-m-d H:i:s', $subscription->time),
59+
];
60+
}
61+
62+
$this->table($headers, $rows);
63+
64+
return self::SUCCESS;
65+
} catch (Throwable $e) {
66+
Log::error("Webhook list error: {$e->getMessage()}", [
67+
'exception' => $e,
68+
]);
69+
$this->error("❌ Webhook list error: {$e->getMessage()}");
70+
71+
return self::FAILURE;
72+
}
73+
}
74+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BushlanovDev\MaxMessengerBot\Laravel\Commands;
6+
7+
use BushlanovDev\MaxMessengerBot\Api;
8+
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
9+
use Illuminate\Console\Command;
10+
use Illuminate\Contracts\Config\Repository as Config;
11+
use Illuminate\Support\Facades\Log;
12+
use Throwable;
13+
14+
/**
15+
* Artisan command for subscribing to webhook updates.
16+
*/
17+
class WebhookSubscribeCommand extends Command
18+
{
19+
/**
20+
* The name and signature of the console command.
21+
*
22+
* @var string
23+
*/
24+
protected $signature = 'maxbot:webhook:subscribe
25+
{url : The webhook URL to subscribe to}
26+
{--secret= : Secret key for webhook verification (optional)}
27+
{--types=* : Update types to subscribe to (optional)}';
28+
29+
/**
30+
* The console command description.
31+
*
32+
* @var string
33+
*/
34+
protected $description = 'Subscribe bot to webhook updates';
35+
36+
/**
37+
* Execute the console command.
38+
*/
39+
public function handle(Api $api, Config $config): int
40+
{
41+
$url = (string)$this->argument('url'); // @phpstan-ignore-line
42+
$secret = $this->option('secret') ?? $config->get('maxbot.webhook_secret');
43+
$types = $this->option('types');
44+
45+
if (!filter_var($url, FILTER_VALIDATE_URL)) {
46+
$this->error('Invalid URL provided.');
47+
48+
return self::FAILURE;
49+
}
50+
51+
$updateTypes = null;
52+
if (is_array($types) && !empty($types)) {
53+
$updateTypes = [];
54+
foreach ($types as $type) {
55+
try {
56+
$updateTypes[] = UpdateType::from($type);
57+
} catch (\ValueError $e) {
58+
$this->error("Invalid update type: $type");
59+
60+
return self::FAILURE;
61+
}
62+
}
63+
}
64+
65+
$this->info('Subscribing to webhook...');
66+
$this->line("URL: $url");
67+
if ($secret) {
68+
$this->line("Secret: " . str_repeat('*', strlen($secret)));
69+
}
70+
if ($updateTypes) {
71+
$this->line("Update types: " . implode(', ', array_map(fn($type) => $type->value, $updateTypes)));
72+
} else {
73+
$this->line("Update types: All (default)");
74+
}
75+
76+
try {
77+
$result = $api->subscribe($url, $secret, $updateTypes);
78+
79+
if ($result->success) {
80+
$this->info('✅ Successfully subscribed to webhook!');
81+
82+
return self::SUCCESS;
83+
} else {
84+
$this->error('❌ Failed to subscribe to webhook.');
85+
$this->line("Response: $result->message");
86+
87+
return self::FAILURE;
88+
}
89+
} catch (Throwable $e) {
90+
Log::error("Webhook subscription error: {$e->getMessage()}", [
91+
'exception' => $e,
92+
]);
93+
$this->error("❌ Webhook subscription error: {$e->getMessage()}");
94+
95+
return self::FAILURE;
96+
}
97+
}
98+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BushlanovDev\MaxMessengerBot\Laravel\Commands;
6+
7+
use BushlanovDev\MaxMessengerBot\Api;
8+
use Illuminate\Console\Command;
9+
use Illuminate\Support\Facades\Log;
10+
use Throwable;
11+
12+
/**
13+
* Artisan command for unsubscribing from webhook updates.
14+
*/
15+
class WebhookUnsubscribeCommand extends Command
16+
{
17+
/**
18+
* The name and signature of the console command.
19+
*
20+
* @var string
21+
*/
22+
protected $signature = 'maxbot:webhook:unsubscribe
23+
{url : The webhook URL to unsubscribe from}
24+
{--confirm : Skip confirmation prompt}';
25+
26+
/**
27+
* The console command description.
28+
*
29+
* @var string
30+
*/
31+
protected $description = 'Unsubscribe bot from webhook updates';
32+
33+
/**
34+
* Execute the console command.
35+
*/
36+
public function handle(Api $api): int
37+
{
38+
$url = (string)$this->argument('url'); // @phpstan-ignore-line
39+
$confirm = $this->option('confirm');
40+
41+
if (!filter_var($url, FILTER_VALIDATE_URL)) {
42+
$this->error('Invalid URL provided.');
43+
44+
return self::FAILURE;
45+
}
46+
47+
if (!$confirm) {
48+
if (!$this->confirm("Are you sure you want to unsubscribe from webhook URL: $url?")) {
49+
$this->info('Operation cancelled.');
50+
51+
return self::SUCCESS;
52+
}
53+
}
54+
55+
$this->info('Unsubscribing from webhook...');
56+
$this->line("URL: $url");
57+
58+
try {
59+
$result = $api->unsubscribe($url);
60+
61+
if ($result->success) {
62+
$this->info('✅ Successfully unsubscribed from webhook!');
63+
64+
return self::SUCCESS;
65+
} else {
66+
$this->error('❌ Failed to unsubscribe from webhook.');
67+
$this->line("Response: $result->message");
68+
69+
return self::FAILURE;
70+
}
71+
} catch (Throwable $e) {
72+
Log::error("Webhook unsubscribe error: {$e->getMessage()}", [
73+
'exception' => $e,
74+
]);
75+
$this->error("❌ Webhook unsubscribe error: {$e->getMessage()}");
76+
77+
return self::FAILURE;
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)