feat: max-bot-test initial — full surface coverage
Some checks failed
Deploy Max Bot Test / deploy (push) Failing after 3s
Some checks failed
Deploy Max Bot Test / deploy (push) Failing after 3s
- Slim 4 PSR-15 webhook, PHP-DI 7, Symfony Console 8 - 13 update handlers, 30+ command scenarios, all 7 button types - Composer pulls pkirillw/max-bot-api-php from GitHub vcs - Multi-stage Dockerfile (php-fpm 8.4-alpine) + nginx - docker-compose.yml targets server (npm_default external) - docker-compose.override.yml for local dev (gitignored) - .gitea/workflows/deploy.yml mirrors ghost-blog-bot pattern Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
198
config/container.php
Normal file
198
config/container.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Bootstrap;
|
||||
use App\Handler\UpdateRouter;
|
||||
use App\Handler\Update\BotAddedToChatHandler;
|
||||
use App\Handler\Update\BotRemovedFromChatHandler;
|
||||
use App\Handler\Update\BotStartedHandler;
|
||||
use App\Handler\Update\BotStopedFromChatHandler;
|
||||
use App\Handler\Update\ChatTitleChangedHandler;
|
||||
use App\Handler\Update\DialogClearedHandler;
|
||||
use App\Handler\Update\DialogRemovedHandler;
|
||||
use App\Handler\Update\MessageCallbackHandler;
|
||||
use App\Handler\Update\MessageCreatedHandler;
|
||||
use App\Handler\Update\MessageEditedHandler;
|
||||
use App\Handler\Update\MessageRemovedHandler;
|
||||
use App\Handler\Update\UserAddedToChatHandler;
|
||||
use App\Handler\Update\UserRemovedFromChatHandler;
|
||||
use App\Keyboard\KeyboardFactory;
|
||||
use App\Logger\LoggerFactory;
|
||||
use App\Scenario\AttachmentScenario;
|
||||
use App\Scenario\BotInfoScenario;
|
||||
use App\Scenario\ChatAdminScenario;
|
||||
use App\Scenario\ChatsScenario;
|
||||
use App\Scenario\DebugScenario;
|
||||
use App\Scenario\HelpScenario;
|
||||
use App\Scenario\KeyboardScenario;
|
||||
use App\Scenario\MessagesScenario;
|
||||
use App\Scenario\PhoneCheckScenario;
|
||||
use App\Scenario\UploadsScenario;
|
||||
use App\Util\CommandRouter;
|
||||
use GuzzleHttp\Client as Guzzle;
|
||||
use Monolog\Logger;
|
||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||
use Pkirillw\MaxBotApi\Api;
|
||||
use Pkirillw\MaxBotApi\Client\Options;
|
||||
use Pkirillw\MaxBotApi\Webhook\WebhookHandler;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
return [
|
||||
'config' => fn () => Bootstrap::envConfig(),
|
||||
|
||||
Psr17Factory::class => fn () => new Psr17Factory(),
|
||||
|
||||
Guzzle::class => fn () => new Guzzle(['timeout' => 30]),
|
||||
|
||||
Options::class => function (ContainerInterface $c): Options {
|
||||
$cfg = $c->get('config');
|
||||
$opts = Options::default()->withKeepRawUpdates(true);
|
||||
if (($cfg['debug.chat_id'] ?? 0) > 0) {
|
||||
$opts = $opts->withDebugChatId((int) $cfg['debug.chat_id']);
|
||||
}
|
||||
return $opts;
|
||||
},
|
||||
|
||||
Api::class => function (ContainerInterface $c): Api {
|
||||
$cfg = $c->get('config');
|
||||
return Api::create(
|
||||
token: (string) $cfg['bot.token'],
|
||||
http: $c->get(Guzzle::class),
|
||||
requestFactory: $c->get(Psr17Factory::class),
|
||||
streamFactory: $c->get(Psr17Factory::class),
|
||||
options: $c->get(Options::class),
|
||||
);
|
||||
},
|
||||
|
||||
LoggerInterface::class => fn (ContainerInterface $c) => LoggerFactory::create($c->get('config')),
|
||||
|
||||
KeyboardFactory::class => function (ContainerInterface $c): KeyboardFactory {
|
||||
$cfg = $c->get('config');
|
||||
$url = trim((string) ($cfg['webapp.url'] ?? ''));
|
||||
return new KeyboardFactory($url !== '' ? $url : null);
|
||||
},
|
||||
|
||||
CommandRouter::class => function (ContainerInterface $c): CommandRouter {
|
||||
$router = new CommandRouter();
|
||||
$router->add('start', $c->get(HelpScenario::class));
|
||||
$router->add('help', $c->get(HelpScenario::class));
|
||||
$router->add('bot', $c->get(BotInfoScenario::class));
|
||||
$router->add('bot_patch', $c->get(BotInfoScenario::class));
|
||||
$router->add('chats', $c->get(ChatsScenario::class));
|
||||
$router->add('chat', $c->get(ChatsScenario::class));
|
||||
$router->add('chat_membership', $c->get(ChatsScenario::class));
|
||||
$router->add('members', $c->get(ChatsScenario::class));
|
||||
$router->add('admins', $c->get(ChatAdminScenario::class));
|
||||
$router->add('pin', $c->get(ChatAdminScenario::class));
|
||||
$router->add('action', $c->get(ChatAdminScenario::class));
|
||||
$router->add('leave', $c->get(ChatAdminScenario::class));
|
||||
$router->add('edit_chat', $c->get(ChatAdminScenario::class));
|
||||
$router->add('add_member', $c->get(ChatAdminScenario::class));
|
||||
$router->add('remove_member', $c->get(ChatAdminScenario::class));
|
||||
$router->add('send_text', $c->get(MessagesScenario::class));
|
||||
$router->add('send_markdown', $c->get(MessagesScenario::class));
|
||||
$router->add('send_html', $c->get(MessagesScenario::class));
|
||||
$router->add('reply', $c->get(MessagesScenario::class));
|
||||
$router->add('forward', $c->get(MessagesScenario::class));
|
||||
$router->add('edit', $c->get(MessagesScenario::class));
|
||||
$router->add('delete', $c->get(MessagesScenario::class));
|
||||
$router->add('messages', $c->get(MessagesScenario::class));
|
||||
$router->add('message', $c->get(MessagesScenario::class));
|
||||
$router->add('markup', $c->get(MessagesScenario::class));
|
||||
$router->add('notify', $c->get(MessagesScenario::class));
|
||||
$router->add('disable_preview', $c->get(MessagesScenario::class));
|
||||
$router->add('keyboard', $c->get(KeyboardScenario::class));
|
||||
$router->add('upload_photo_file', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_photo_url', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_photo_bytes', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_photo_base64', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_audio', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_video', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_file', $c->get(UploadsScenario::class));
|
||||
$router->add('upload_url', $c->get(UploadsScenario::class));
|
||||
$router->add('attach_location', $c->get(AttachmentScenario::class));
|
||||
$router->add('attach_contact', $c->get(AttachmentScenario::class));
|
||||
$router->add('attach_sticker', $c->get(AttachmentScenario::class));
|
||||
$router->add('phone_check', $c->get(PhoneCheckScenario::class));
|
||||
$router->add('phone_list', $c->get(PhoneCheckScenario::class));
|
||||
$router->add('debug', $c->get(DebugScenario::class));
|
||||
return $router;
|
||||
},
|
||||
|
||||
UpdateRouter::class => function (ContainerInterface $c): UpdateRouter {
|
||||
$router = new UpdateRouter($c->get(LoggerInterface::class));
|
||||
$router->add($c->get(MessageCreatedHandler::class));
|
||||
$router->add($c->get(MessageCallbackHandler::class));
|
||||
$router->add($c->get(MessageEditedHandler::class));
|
||||
$router->add($c->get(MessageRemovedHandler::class));
|
||||
$router->add($c->get(BotStartedHandler::class));
|
||||
$router->add($c->get(BotAddedToChatHandler::class));
|
||||
$router->add($c->get(BotRemovedFromChatHandler::class));
|
||||
$router->add($c->get(BotStopedFromChatHandler::class));
|
||||
$router->add($c->get(UserAddedToChatHandler::class));
|
||||
$router->add($c->get(UserRemovedFromChatHandler::class));
|
||||
$router->add($c->get(ChatTitleChangedHandler::class));
|
||||
$router->add($c->get(DialogClearedHandler::class));
|
||||
$router->add($c->get(DialogRemovedHandler::class));
|
||||
return $router;
|
||||
},
|
||||
|
||||
WebhookHandler::class => function (ContainerInterface $c): WebhookHandler {
|
||||
$cfg = $c->get('config');
|
||||
$handler = new WebhookHandler(
|
||||
responseFactory: $c->get(Psr17Factory::class),
|
||||
secret: (string) $cfg['bot.secret'],
|
||||
keepRawUpdates: true,
|
||||
);
|
||||
$router = $c->get(UpdateRouter::class);
|
||||
$logger = $c->get(LoggerInterface::class);
|
||||
$handler->setHandler(static function (object $update) use ($router, $logger): void {
|
||||
try {
|
||||
$router->dispatch($update);
|
||||
} catch (\Throwable $e) {
|
||||
$logger->error('Update failed: ' . $e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
});
|
||||
return $handler;
|
||||
},
|
||||
|
||||
HelpScenario::class => \DI\autowire(),
|
||||
BotInfoScenario::class => \DI\autowire(),
|
||||
ChatsScenario::class => \DI\autowire(),
|
||||
ChatAdminScenario::class => \DI\autowire(),
|
||||
MessagesScenario::class => \DI\autowire(),
|
||||
UploadsScenario::class => \DI\autowire(),
|
||||
KeyboardScenario::class => \DI\autowire(),
|
||||
AttachmentScenario::class => \DI\autowire(),
|
||||
PhoneCheckScenario::class => \DI\autowire(),
|
||||
DebugScenario::class => \DI\autowire(),
|
||||
|
||||
MessageCreatedHandler::class => \DI\autowire(),
|
||||
MessageCallbackHandler::class => \DI\autowire(),
|
||||
MessageEditedHandler::class => \DI\autowire(),
|
||||
MessageRemovedHandler::class => \DI\autowire(),
|
||||
BotStartedHandler::class => \DI\autowire(),
|
||||
BotAddedToChatHandler::class => \DI\autowire(),
|
||||
BotRemovedFromChatHandler::class => \DI\autowire(),
|
||||
BotStopedFromChatHandler::class => \DI\autowire(),
|
||||
UserAddedToChatHandler::class => \DI\autowire(),
|
||||
UserRemovedFromChatHandler::class => \DI\autowire(),
|
||||
ChatTitleChangedHandler::class => \DI\autowire(),
|
||||
DialogClearedHandler::class => \DI\autowire(),
|
||||
DialogRemovedHandler::class => \DI\autowire(),
|
||||
|
||||
Application::class => function (ContainerInterface $c): Application {
|
||||
$app = new Application('max-bot-test', '1.0.0');
|
||||
$app->addCommands([
|
||||
$c->get(\App\Command\SubscribeWebhookCommand::class),
|
||||
$c->get(\App\Command\UnsubscribeWebhookCommand::class),
|
||||
$c->get(\App\Command\ListSubscriptionsCommand::class),
|
||||
$c->get(\App\Command\GetBotInfoCommand::class),
|
||||
$c->get(\App\Command\SelfTestCommand::class),
|
||||
]);
|
||||
return $app;
|
||||
},
|
||||
];
|
||||
19
config/routes.php
Normal file
19
config/routes.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||
use Pkirillw\MaxBotApi\Webhook\WebhookHandler;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
return static function (App $app): void {
|
||||
$app->post('/webhook', WebhookHandler::class);
|
||||
|
||||
$app->get('/health', function (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
|
||||
$payload = json_encode(['ok' => true, 'time' => date('c')], JSON_UNESCAPED_UNICODE);
|
||||
$response->getBody()->write((string) $payload);
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user