diff --git a/src/TgBotLib/Bot.php b/src/TgBotLib/Bot.php index e024d34..0b48e37 100644 --- a/src/TgBotLib/Bot.php +++ b/src/TgBotLib/Bot.php @@ -18,6 +18,7 @@ use TgBotLib\Enums\Methods; use TgBotLib\Enums\Types\ChatActionType; use TgBotLib\Enums\Types\ParseMode; + use TgBotLib\Events\CommandEvent; use TgBotLib\Exceptions\TelegramException; use TgBotLib\Objects\BotCommand; use TgBotLib\Objects\BotCommandScope; @@ -90,7 +91,7 @@ * @method string exportChatInviteLink(string|int $chat_id) Use this method to generate a new primary invite link for a chat; any previously generated primary link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the new invite link as String on success. * @method ChatInviteLink createChatInviteLink(string|int $chat_id, ?string $name=null, ?int $expire_date=null, ?int $member_limit=null, ?bool $creates_join_request=null) Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. The link can be revoked using the method revokeChatInviteLink. Returns the new invite link as ChatInviteLink object. * @method ChatInviteLink editChatInviteLink(string|int $chat_id, string $invite_link, ?string $name=null, ?int $expire_date=null, ?int $member_limit=null, ?bool $creates_join_request=null) Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the edited invite link as a ChatInviteLink object. - * @method ChatInviteLink createChatSubscriptionInviteLink(string|int $chat_id, int $subscription_period, int $subscription_price, ?string $name=null,) Use this method to create a subscription invite link for a channel chat. The bot must have the can_invite_users administrator rights. The link can be edited using the method editChatSubscriptionInviteLink or revoked using the method revokeChatInviteLink. Returns the new invite link as a ChatInviteLink object. + * @method ChatInviteLink createChatSubscriptionInviteLink(string|int $chat_id, int $subscription_period, int $subscription_price, ?string $name=null) Use this method to create a subscription invite link for a channel chat. The bot must have the can_invite_users administrator rights. The link can be edited using the method editChatSubscriptionInviteLink or revoked using the method revokeChatInviteLink. Returns the new invite link as a ChatInviteLink object. * @method ChatInviteLink editChatSubscriptionInviteLink(string|int $chat_id, string $invite_link, ?string $name=null) Use this method to edit a subscription invite link created by the bot. The bot must have the can_invite_users administrator rights. Returns the edited invite link as a ChatInviteLink object. * @method ChatInviteLink revokeChatInviteLink(string|int $chat_id, string $invite_link) Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new link is automatically generated. The bot must be an administrator in the chat for this to work and must have the appropriate administrator rights. Returns the revoked invite link as ChatInviteLink object. * @method bool approveChatJoinRequest(string|int $chat_id, int $user_id) Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success. @@ -348,6 +349,59 @@ }); } + /** + * Handles an incoming update. It first checks if there is a command within the update and + * processes it with appropriate event handlers. If no command is present, it determines the + * type of the update and processes it with corresponding event handlers. If no specific event + * handlers are found for the update type, it falls back to a generic update event handler. + * + * @param Update $update The update object to be handled. + * @return void + */ + public function handleUpdate(Update $update): void + { + $command = $update?->getAnyMessage()?->getCommand(); + if($command !== null) + { + $commandExecuted = false; + + /** @var CommandEvent $eventHandler */ + foreach($this->getEventHandlersByCommand($command) as $eventHandler) + { + (new $eventHandler($update))->handle($this); + $commandExecuted = true; + } + + if($commandExecuted) + { + return; + } + } + + // Get event handlers by type, determine the type of the update + $eventHandlers = $this->getEventHandlersByType(EventType::determineEventType($update)); + + // If there are no appropriate event handlers for the update type, use the generic update event handler + if(empty($eventHandlers)) + { + foreach ($this->getEventHandlersByType(EventType::UPDATE_EVENT) as $eventHandler) + { + /** @var UpdateEvent $eventHandler */ + (new $eventHandler($update))->handle($this); + } + + // We return early here to avoid executing the generic update event handler twice + return; + } + + // Execute all event handlers that match the update type + /** @var UpdateEvent $eventHandler */ + foreach($eventHandlers as $eventHandler) + { + (new $eventHandler($update))->handle($this); + } + } + /** * Sends a request by executing the specified method with the given parameters. * diff --git a/src/TgBotLib/Events/CommandEvent.php b/src/TgBotLib/Events/CommandEvent.php index f5ce420..dd21f2d 100644 --- a/src/TgBotLib/Events/CommandEvent.php +++ b/src/TgBotLib/Events/CommandEvent.php @@ -34,7 +34,7 @@ */ protected function getMessage(): Message { - return $this->update->getMessage(); + return $this->update->getAnyMessage(); } /** @@ -44,12 +44,12 @@ */ protected function getArguments(): string { - if(strlen($this->getMessage()->getText()) <= strlen(static::getCommand()) + 1) + if(strlen($this->getMessage()->getCommand()) <= strlen(static::getCommand()) + 1) { return ''; } - return substr($this->getMessage()->getText(), strlen(static::getCommand()) + 1); + return substr($this->getMessage()->getCommand(), strlen(static::getCommand()) + 1); } /** diff --git a/src/TgBotLib/Objects/Message.php b/src/TgBotLib/Objects/Message.php index 2f5714f..cc5b2c3 100644 --- a/src/TgBotLib/Objects/Message.php +++ b/src/TgBotLib/Objects/Message.php @@ -1008,20 +1008,24 @@ */ public function getCommand(): ?string { - if ($this->text === null) + if ($this->getAnyText() === null) { return null; } - $text = trim($this->text); + $text = trim($this->getAnyText()); if (!str_starts_with($text, '/')) { return null; } - $parts = explode(' ', $text); - return ltrim($parts[0], '/'); + if (preg_match('/^\/([a-zA-Z0-9_]+)(?:@[a-zA-Z0-9_]+)?/', $text, $matches)) + { + return $matches[1]; + } + + return null; } /** diff --git a/src/TgBotLib/PollingBot.php b/src/TgBotLib/PollingBot.php index 6632a46..1012057 100644 --- a/src/TgBotLib/PollingBot.php +++ b/src/TgBotLib/PollingBot.php @@ -3,12 +3,6 @@ namespace TgBotLib; use RuntimeException; - use TgBotLib\Abstracts\UpdateEvent; - use TgBotLib\Classes\Logger; - use TgBotLib\Enums\EventType; - use TgBotLib\Events\CommandEvent; - use TgBotLib\Objects\Update; - use Throwable; /** * PollingBot class that extends Bot for handling updates using polling. @@ -181,7 +175,10 @@ } else { - $this->processUpdates($updates); + foreach($updates as $update) + { + $this->handleUpdate($update); + } } } @@ -209,67 +206,12 @@ else { // Child process - try + foreach($updates as $update) { - $this->processUpdates($updates); - exit(0); - } - catch (Throwable $e) - { - Logger::getLogger()->error("Error in forked process: " . $e->getMessage()); - exit(1); - } - } - } - - /** - * Processes a batch of updates with appropriate event handlers - * - * @param array $updates - * @return void - */ - private function processUpdates(array $updates): void - { - /** @var Update $update */ - foreach ($updates as $update) - { - // Check if the update contains a command - $command = $update?->getAnyMessage()?->getCommand(); - $commandExecuted = false; - if ($command !== null) - { - foreach ($this->getEventHandlersByCommand($command) as $commandHandler) - { - (new $commandHandler($update))->handle($this); - $commandExecuted = true; - } - - if($commandExecuted) - { - continue; - } + $this->handleUpdate($update); } - // Check if the update contains a callback query - $updateByType = $this->getEventHandlersByType(EventType::determineEventType($update)); - if (count($updateByType) === 0) - { - // If no event handlers are found appropriate for the update type, use the generic update event handler - foreach ($this->getEventHandlersByType(EventType::UPDATE_EVENT) as $eventHandler) - { - /** @var UpdateEvent $eventHandler */ - (new $eventHandler($update))->handle($this); - } - } - else - { - // Otherwise, use the appropriate event handler for the update type - foreach ($updateByType as $eventHandler) - { - /** @var UpdateEvent $eventHandler */ - (new $eventHandler($update))->handle($this); - } - } + exit(0); } }