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:
154
src/Scenario/UploadsScenario.php
Normal file
154
src/Scenario/UploadsScenario.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scenario;
|
||||
|
||||
use App\Util\ParsedCommand;
|
||||
use Pkirillw\MaxBotApi\Builder\Message as MessageBuilder;
|
||||
use Pkirillw\MaxBotApi\Scheme\Attachment\PhotoTokens;
|
||||
use Pkirillw\MaxBotApi\Scheme\Attachment\UploadedInfo;
|
||||
use Pkirillw\MaxBotApi\Scheme\Enum\Format;
|
||||
use Pkirillw\MaxBotApi\Scheme\Enum\UploadType;
|
||||
use Pkirillw\MaxBotApi\Scheme\Update\MessageCreatedUpdate;
|
||||
|
||||
final class UploadsScenario extends AbstractScenario
|
||||
{
|
||||
private const DEFAULT_PHOTO_URL = 'https://placehold.co/600x400/png';
|
||||
private const DEFAULT_FILE_URL = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';
|
||||
|
||||
public function handle(MessageCreatedUpdate $update, ParsedCommand $command): void
|
||||
{
|
||||
switch ($command->name) {
|
||||
case 'upload_photo_file':
|
||||
$this->uploadPhotoFromFile($update, $command);
|
||||
return;
|
||||
case 'upload_photo_url':
|
||||
$this->uploadPhotoFromUrl($update, $command);
|
||||
return;
|
||||
case 'upload_photo_bytes':
|
||||
$this->uploadPhotoFromBytes($update);
|
||||
return;
|
||||
case 'upload_photo_base64':
|
||||
$this->uploadPhotoFromBase64($update);
|
||||
return;
|
||||
case 'upload_audio':
|
||||
$this->uploadMediaFromFile($update, $command, UploadType::Audio);
|
||||
return;
|
||||
case 'upload_video':
|
||||
$this->uploadMediaFromFile($update, $command, UploadType::Video);
|
||||
return;
|
||||
case 'upload_file':
|
||||
$this->uploadMediaFromFile($update, $command, UploadType::File);
|
||||
return;
|
||||
case 'upload_url':
|
||||
$this->uploadMediaFromUrl($update, $command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private function uploadPhotoFromFile(MessageCreatedUpdate $update, ParsedCommand $command): void
|
||||
{
|
||||
$path = $command->arg(0, __DIR__ . '/../../storage/uploads/photo.png');
|
||||
if (!is_file($path)) {
|
||||
$this->replyMarkdown($update, "File not found: `$path`. Положи PNG в storage/uploads/photo.png.");
|
||||
return;
|
||||
}
|
||||
$tokens = $this->api->uploads->uploadPhotoFromFile($path);
|
||||
$this->sendPhotoWithCaption($update, $tokens, 'photo from file');
|
||||
}
|
||||
|
||||
private function uploadPhotoFromUrl(MessageCreatedUpdate $update, ParsedCommand $command): void
|
||||
{
|
||||
$url = $command->arg(0, self::DEFAULT_PHOTO_URL);
|
||||
$tokens = $this->api->uploads->uploadPhotoFromUrl($url);
|
||||
$this->sendPhotoWithCaption($update, $tokens, "photo from $url");
|
||||
}
|
||||
|
||||
private function uploadPhotoFromBytes(MessageCreatedUpdate $update): void
|
||||
{
|
||||
// Synthesize a 1x1 PNG via PNG header bytes (no GD dependency).
|
||||
$png = base64_decode(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=',
|
||||
strict: true,
|
||||
);
|
||||
$tokens = $this->api->uploads->uploadPhotoFromBytes($png, 'pixel.png');
|
||||
$this->sendPhotoWithCaption($update, $tokens, 'photo from raw bytes');
|
||||
}
|
||||
|
||||
private function uploadPhotoFromBase64(MessageCreatedUpdate $update): void
|
||||
{
|
||||
$base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
||||
$tokens = $this->api->uploads->uploadPhotoFromBase64($base64, 'pixel.png');
|
||||
$this->sendPhotoWithCaption($update, $tokens, 'photo from base64');
|
||||
}
|
||||
|
||||
private function uploadMediaFromFile(MessageCreatedUpdate $update, ParsedCommand $command, UploadType $type): void
|
||||
{
|
||||
$default = __DIR__ . '/../../storage/uploads/' . match ($type) {
|
||||
UploadType::Audio => 'audio.mp3',
|
||||
UploadType::Video => 'video.mp4',
|
||||
UploadType::File => 'doc.pdf',
|
||||
UploadType::Photo => 'photo.png',
|
||||
};
|
||||
$path = $command->arg(0, $default);
|
||||
if (!is_file($path)) {
|
||||
$this->replyMarkdown($update, "File not found: `$path`. Положи файл в storage/uploads/.");
|
||||
return;
|
||||
}
|
||||
$info = $this->api->uploads->uploadMediaFromFile($type, $path);
|
||||
$this->sendMediaWithCaption($update, $info, $type);
|
||||
}
|
||||
|
||||
private function uploadMediaFromUrl(MessageCreatedUpdate $update, ParsedCommand $command): void
|
||||
{
|
||||
$typeStr = $command->arg(0);
|
||||
$url = $command->arg(1, self::DEFAULT_FILE_URL);
|
||||
if ($typeStr === null) {
|
||||
$this->replyMarkdown($update, 'Usage: `/upload_url <audio|video|file> [url]`');
|
||||
return;
|
||||
}
|
||||
$type = UploadType::tryFrom($typeStr);
|
||||
if ($type === null || $type === UploadType::Photo) {
|
||||
$this->replyMarkdown($update, 'Type must be audio, video or file. Use /upload_photo_url for photos.');
|
||||
return;
|
||||
}
|
||||
$info = $this->api->uploads->uploadMediaFromUrl($type, $url);
|
||||
$this->sendMediaWithCaption($update, $info, $type);
|
||||
}
|
||||
|
||||
private function sendPhotoWithCaption(MessageCreatedUpdate $update, PhotoTokens $tokens, string $caption): void
|
||||
{
|
||||
if ($tokens->photos === []) {
|
||||
$this->replyMarkdown($update, 'Upload returned no photo tokens (server may still be processing).');
|
||||
return;
|
||||
}
|
||||
$this->api->messages->send(
|
||||
MessageBuilder::new()
|
||||
->setChat($update->getChatId())
|
||||
->setText($caption)
|
||||
->setFormat(Format::Markdown)
|
||||
->addPhoto($tokens),
|
||||
);
|
||||
}
|
||||
|
||||
private function sendMediaWithCaption(MessageCreatedUpdate $update, UploadedInfo $info, UploadType $type): void
|
||||
{
|
||||
$caption = match ($type) {
|
||||
UploadType::Audio => 'audio upload',
|
||||
UploadType::Video => 'video upload',
|
||||
UploadType::File => 'file upload',
|
||||
UploadType::Photo => 'photo upload',
|
||||
};
|
||||
$builder = MessageBuilder::new()
|
||||
->setChat($update->getChatId())
|
||||
->setText($caption . ' (token=' . $info->token . ')');
|
||||
match ($type) {
|
||||
UploadType::Audio => $builder->addAudio($info),
|
||||
UploadType::Video => $builder->addVideo($info),
|
||||
UploadType::File => $builder->addFile($info),
|
||||
UploadType::Photo => $builder,
|
||||
};
|
||||
$this->api->messages->send($builder);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user