Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,29 @@ $botEventListener->onCommand(
);

// Start long polling (must be called after all handlers are registered)
$botEventListener->listen(pollTime: 30);
$botEventListener->listen(
pollTime: 30,
onException: function (
\Exception $exception,
\BelkaTech\VkTeamsBot\Event\EventDto $event
): void {
// Log the error
$this->logger->error('Some text', [
'event_id' => $event->eventId,
'event_type' => $event->type,
'event_payload' => $event->payload,
'exception' => $exception,
]);
error_log($exception->getMessage());

// Or catch exception to an error reporting system
$this->sentry->captureException($exception);

// On exception loop continues,
// you can re-throw the exception to force stop the loop
throw $exception;
},
);

// Stop the listener programmatically (e.g. from a handler)
$botEventListener->stop();
Expand Down
13 changes: 9 additions & 4 deletions src/Api/ChatsApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,16 @@ public function getAdmins(
*/
public function getMembers(
string $chatId,
?string $cursor = null,
): array {
$params = ['chatId' => $chatId];

if ($cursor !== null) {
$params['cursor'] = $cursor;
}

/** @phpstan-ignore return.type */
return $this->httpClient->get('/v1/chats/getMembers', [
'chatId' => $chatId,
]);
return $this->httpClient->get('/v1/chats/getMembers', $params);
}

/**
Expand Down Expand Up @@ -283,7 +288,7 @@ public function setAvatar(
string $imagePath,
): array {
/** @phpstan-ignore return.type */
return $this->httpClient->post(
return $this->httpClient->postMultipart(
'/v1/chats/avatar/set',
['chatId' => $chatId],
$imagePath,
Expand Down
18 changes: 9 additions & 9 deletions src/Api/MessagesApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public function sendText(
'text' => $text,
'replyMsgId' => $replyMsgId,
'forwardChatId' => $forwardChatId,
'forwardMsgId' => json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR),
'inlineKeyboardMarkup' => json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR),
'forwardMsgId' => $forwardMsgId !== null ? json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR) : null,
'inlineKeyboardMarkup' => $inlineKeyboardMarkup !== null ? json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR) : null,
'format' => $format,
'parseMode' => ($parseMode ?? $this->parseMode)->value,
]);
Expand Down Expand Up @@ -127,15 +127,15 @@ public function sendVoice(
'fileId' => $fileId,
'replyMsgId' => $replyMsgId,
'forwardChatId' => $forwardChatId,
'forwardMsgId' => json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR),
'inlineKeyboardMarkup' => json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR),
'forwardMsgId' => $forwardMsgId !== null ? json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR) : null,
'inlineKeyboardMarkup' => $inlineKeyboardMarkup !== null ? json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR) : null,
];

if ($filePath !== null) {
unset($params['fileId']);

/** @phpstan-ignore return.type */
return $this->httpClient->post(
return $this->httpClient->postMultipart(
'/v1/messages/sendVoice',
$params,
$filePath,
Expand Down Expand Up @@ -170,7 +170,7 @@ public function editText(
'chatId' => $chatId,
'msgId' => $msgId,
'text' => $text,
'inlineKeyboardMarkup' => json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR),
'inlineKeyboardMarkup' => $inlineKeyboardMarkup !== null ? json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR) : null,
'format' => $format,
'parseMode' => ($parseMode ?? $this->parseMode)->value,
]);
Expand Down Expand Up @@ -299,15 +299,15 @@ private function sendMedia(
'caption' => $caption,
'replyMsgId' => $replyMsgId,
'forwardChatId' => $forwardChatId,
'forwardMsgId' => json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR),
'inlineKeyboardMarkup' => json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR),
'forwardMsgId' => $forwardMsgId !== null ? json_encode($forwardMsgId, flags: JSON_THROW_ON_ERROR) : null,
'inlineKeyboardMarkup' => $inlineKeyboardMarkup !== null ? json_encode($inlineKeyboardMarkup, flags: JSON_THROW_ON_ERROR) : null,
'format' => $format,
'parseMode' => ($parseMode ?? $this->parseMode)->value,
];

if ($filePath !== null) {
/** @phpstan-ignore return.type */
return $this->httpClient->post(
return $this->httpClient->postMultipart(
$endpoint,
$params,
$filePath,
Expand Down
47 changes: 34 additions & 13 deletions src/BotEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public function onCommand(
string $command,
\Closure $handler,
): void {
if (isset($this->commandHandlers[$command])) {
throw new InvalidArgumentException(
"Command handler for '{$command}' is already registered",
);
}

$this->commandHandlers[$command] = $handler;
}

Expand Down Expand Up @@ -113,9 +119,11 @@ public function stop(): void

/**
* @param int $pollTime Maximum polling request duration (1-60 sec)
* @param ?\Closure(\Exception, EventDto): void $onException
*/
public function listen(
int $pollTime,
?\Closure $onException = null,
): void {
if ($pollTime < 1 || $pollTime > 60) {
throw new InvalidArgumentException(
Expand Down Expand Up @@ -144,23 +152,36 @@ public function listen(
payload: $event['payload'],
);

if ($eventType === EventTypeEnum::MessageNew) {
$text = isset($event['payload']['text']) && is_string($event['payload']['text'])
? $event['payload']['text']
: '';
foreach ($this->commandHandlers as $command => $handler) {
if (str_starts_with($text, $command)) {
$handler($this->bot, $eventDto);

continue 2;
try {
if ($eventType === EventTypeEnum::MessageNew) {
$text = isset($event['payload']['text']) && is_string($event['payload']['text'])
? $event['payload']['text']
: '';

foreach ($this->commandHandlers as $command => $handler) {
if (
$text === $command
|| str_starts_with($text, $command . ' ')
|| str_starts_with($text, $command . '@')
) {
$handler($this->bot, $eventDto);

continue 2;
}
}
}
}

if (array_key_exists($eventType->value, $this->handlers)) {
foreach ($this->handlers[$eventType->value] as $handler) {
$handler($this->bot, $eventDto);
if (array_key_exists($eventType->value, $this->handlers)) {
foreach ($this->handlers[$eventType->value] as $handler) {
$handler($this->bot, $eventDto);
}
}
} catch (\Exception $e) {
if ($onException === null) {
throw $e;
}

$onException($e, $eventDto);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Http/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function get(
*
* @throws ClientExceptionInterface
*/
public function post(
public function postMultipart(
string $path,
array $params,
string $filePath,
Expand Down Expand Up @@ -110,7 +110,7 @@ private function filterParams(
): array {
return array_filter(
$params,
static fn(mixed $value): bool => $value !== null && $value !== 'null',
static fn(mixed $value): bool => $value !== null,
);
}
}
2 changes: 1 addition & 1 deletion src/Keyboard/Keyboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
final class Keyboard implements \JsonSerializable
{
/** @var list<list<Button>> */
public array $rows = [];
private array $rows = [];

/**
* @param list<Button> $row
Expand Down
61 changes: 61 additions & 0 deletions test/Spy/HttpClientSpy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace BelkaTech\VkTeamsBot\Test\Spy;

use BelkaTech\VkTeamsBot\Http\HttpClient;

final class HttpClientSpy extends HttpClient
{
/**
* @var list<array{
* string,
* string,
* array<string, mixed>,
* }|array{
* string,
* string,
* array<string, mixed>,
* string,
* }>
*/
public array $calls = [];

/** @var array<string, mixed> */
private array $getResponse;

/** @var array<string, mixed> */
private array $postMultipartResponse;

/**
* @param array<string, mixed> $getResponse
* @param array<string, mixed> $postMultipartResponse
*/
public function __construct(
array $getResponse = ['ok' => true],
array $postMultipartResponse = ['ok' => true],
) {
$this->getResponse = $getResponse;
$this->postMultipartResponse = $postMultipartResponse;
}

public function get(
string $path,
array $params = [],
): array {
$this->calls[] = ['get', $path, $params];

return $this->getResponse;
}

public function postMultipart(
string $path,
array $params,
string $filePath,
): array {
$this->calls[] = ['postMultipart', $path, $params, $filePath];

return $this->postMultipartResponse;
}
}
Loading
Loading