logger = $logger ?? new NullLogger(); } /** * Simple response to request. * * @param array $data * * @return Result * @throws ReflectionException */ public function createResult(array $data): Result { return Result::fromArray($data); } /** * Information about the current bot. * * @param array $data * * @return BotInfo * @throws ReflectionException */ public function createBotInfo(array $data): BotInfo { return BotInfo::fromArray($data); } /** * Information about webhook subscription. * * @param array $data * * @return Subscription * @throws ReflectionException */ public function createSubscription(array $data): Subscription { return Subscription::fromArray($data); } /** * List of all active webhook subscriptions. * * @param array $data * * @return Subscription[] * @throws ReflectionException */ public function createSubscriptions(array $data): array { return isset($data['subscriptions']) && is_array($data['subscriptions']) ? array_map([$this, 'createSubscription'], $data['subscriptions']) : []; } /** * Creates a Message from the specific response structure of the sendMessage endpoint. * * @param array $data The raw response from the client. * * @return Message * @throws ReflectionException */ public function createMessageFromSendResponse(array $data): Message { $messageData = $data['message']; $topLevelData = [ 'chat_id' => $data['chat_id'] ?? null, 'recipient_id' => $data['recipient_id'] ?? null, 'message_id' => $data['message_id'] ?? null, ]; $messageData = array_merge($messageData, array_filter($topLevelData, fn($value) => $value !== null)); if (isset($messageData['message']) && is_array($messageData['message'])) { $messageData['body'] = $messageData['message']; unset($messageData['message']); } return $this->createMessage($messageData); } /** * Message. * * @param array $data * * @return Message * @throws ReflectionException */ public function createMessage(array $data): Message { if (isset($data['body']) && is_array($data['body'])) { $data['body'] = $this->createMessageBody($data['body']); } return Message::fromArray($data); } /** * List of messages. * * @param array $data * * @return Message[] */ public function createMessages(array $data): array { return isset($data['messages']) && is_array($data['messages']) ? array_map([$this, 'createMessage'], $data['messages']) : []; } /** * Creates a MessageBody object from raw API data, handling polymorphic attachments and markup. * * @param array $data * * @return MessageBody * @throws ReflectionException */ private function createMessageBody(array $data): MessageBody { if (isset($data['attachments']) && is_array($data['attachments'])) { $data['attachments'] = array_map( [$this, 'createAttachment'], $data['attachments'], ); } if (isset($data['markup']) && is_array($data['markup'])) { $data['markup'] = array_map( [$this, 'createMarkupElement'], $data['markup'], ); } return MessageBody::fromArray($data); } /** * Creates a specific Attachment model based on the 'type' field. * * @param array $data * * @return AbstractAttachment * @throws ReflectionException */ public function createAttachment(array $data): AbstractAttachment { $attachmentType = AttachmentType::tryFrom($data['type'] ?? ''); if ($attachmentType === AttachmentType::InlineKeyboard && isset($data['payload']['buttons']) && is_array($data['payload']['buttons'])) { $data['payload']['buttons'] = array_map( fn($rowOfButtons) => array_map([$this, 'createInlineButton'], $rowOfButtons), $data['payload']['buttons'] ); } return match ($attachmentType) { AttachmentType::Image => PhotoAttachment::fromArray($data), AttachmentType::Video => VideoAttachment::fromArray($data), AttachmentType::Audio => AudioAttachment::fromArray($data), AttachmentType::File => FileAttachment::fromArray($data), AttachmentType::Sticker => StickerAttachment::fromArray($data), AttachmentType::Contact => ContactAttachment::fromArray($data), AttachmentType::InlineKeyboard => InlineKeyboardAttachment::fromArray($data), AttachmentType::Location => LocationAttachment::fromArray($data), AttachmentType::Share => ShareAttachment::fromArray($data), default => throw new LogicException('Unknown or unsupported attachment type: ' . ($data['type'] ?? 'none')), }; } /** * Creates a specific ReplyButton model based on the 'type' field. * * @param array $data * * @return AbstractReplyButton * @throws ReflectionException * @throws LogicException */ public function createReplyButton(array $data): AbstractReplyButton { return match (ReplyButtonType::tryFrom($data['type'] ?? '')) { ReplyButtonType::Message => SendMessageButton::fromArray($data), ReplyButtonType::UserContact => SendContactButton::fromArray($data), ReplyButtonType::UserGeoLocation => SendGeoLocationButton::fromArray($data), default => throw new LogicException( 'Unknown or unsupported reply button type: ' . ($data['type'] ?? 'none') ), }; } /** * Creates a specific InlineButton model based on the 'type' field. * * @param array $data * @return AbstractInlineButton * @throws ReflectionException * @throws LogicException */ public function createInlineButton(array $data): AbstractInlineButton { return match (InlineButtonType::tryFrom($data['type'] ?? '')) { InlineButtonType::Callback => CallbackButton::fromArray($data), InlineButtonType::Link => LinkButton::fromArray($data), InlineButtonType::RequestContact => RequestContactButton::fromArray($data), InlineButtonType::RequestGeoLocation => RequestGeoLocationButton::fromArray($data), InlineButtonType::Chat => ChatButton::fromArray($data), InlineButtonType::OpenApp => OpenAppButton::fromArray($data), InlineButtonType::Clipboard => ClipboardButton::fromArray($data), InlineButtonType::Message => MessageButton::fromArray($data), default => throw new LogicException( 'Unknown or unsupported inline button type: ' . ($data['type'] ?? 'none') ), }; } /** * Endpoint you should upload to your binaries. * * @param array $data * * @return UploadEndpoint * @throws ReflectionException */ public function createUploadEndpoint(array $data): UploadEndpoint { return UploadEndpoint::fromArray($data); } /** * Chat information. * * @param array $data * * @return Chat * @throws ReflectionException */ public function createChat(array $data): Chat { return Chat::fromArray($data); } /** * Creates a list of updates from a raw API response. * * @param array $data Raw response data. * * @return UpdateList * @throws ReflectionException * @throws LogicException */ public function createUpdateList(array $data): UpdateList { $updateObjects = []; if (isset($data['updates']) && is_array($data['updates'])) { foreach ($data['updates'] as $updateData) { // Here we delegate the creation of a specific update to another factory method try { $updateObjects[] = $this->createUpdate($updateData); } catch (LogicException $e) { $this->logger->debug($e->getMessage(), ['payload' => $updateData, 'exception' => $e]); } } } return new UpdateList( $updateObjects, $data['marker'] ? (int)$data['marker'] : null, ); } /** * Creates a specific Update model based on the 'update_type' field. * * @param array $data Raw data for a single update. * * @return AbstractUpdate * @throws ReflectionException * @throws LogicException */ public function createUpdate(array $data): AbstractUpdate { return match (UpdateType::tryFrom($data['update_type'] ?? '')) { UpdateType::MessageCreated => MessageCreatedUpdate::fromArray($data), UpdateType::MessageCallback => MessageCallbackUpdate::fromArray($data), UpdateType::MessageEdited => MessageEditedUpdate::fromArray($data), UpdateType::MessageRemoved => MessageRemovedUpdate::fromArray($data), UpdateType::BotAdded => BotAddedToChatUpdate::fromArray($data), UpdateType::BotRemoved => BotRemovedFromChatUpdate::fromArray($data), UpdateType::DialogMuted => DialogMutedUpdate::fromArray($data), UpdateType::DialogUnmuted => DialogUnmutedUpdate::fromArray($data), UpdateType::DialogCleared => DialogClearedUpdate::fromArray($data), UpdateType::DialogRemoved => DialogRemovedUpdate::fromArray($data), UpdateType::UserAdded => UserAddedToChatUpdate::fromArray($data), UpdateType::UserRemoved => UserRemovedFromChatUpdate::fromArray($data), UpdateType::BotStarted => BotStartedUpdate::fromArray($data), UpdateType::BotStopped => BotStoppedUpdate::fromArray($data), UpdateType::ChatTitleChanged => ChatTitleChangedUpdate::fromArray($data), UpdateType::MessageChatCreated => MessageChatCreatedUpdate::fromArray($data), default => throw new LogicException( 'Unknown or unsupported update type received: ' . ($data['update_type'] ?? 'none') ), }; } /** * Information about chat list. * * @param array $data * * @return ChatList * @throws ReflectionException */ public function createChatList(array $data): ChatList { return ChatList::fromArray($data); } /** * Creates a ChatMember object from raw API data. * * @param array $data * * @return ChatMember * @throws ReflectionException */ public function createChatMember(array $data): ChatMember { return ChatMember::fromArray($data); } /** * Creates a ChatMembersList object from raw API data. * * @param array $data * * @return ChatMembersList * @throws ReflectionException */ public function createChatMembersList(array $data): ChatMembersList { return ChatMembersList::fromArray($data); } /** * Creates a VideoAttachmentDetails object from raw API data. * * @param array $data * * @return VideoAttachmentDetails * @throws ReflectionException */ public function createVideoAttachmentDetails(array $data): VideoAttachmentDetails { return VideoAttachmentDetails::fromArray($data); } /** * Creates a specific Markup model based on the 'type' field. * * @param array $data * * @return AbstractMarkup * @throws ReflectionException */ public function createMarkupElement(array $data): AbstractMarkup { return match (MarkupType::tryFrom($data['type'] ?? '')) { MarkupType::Strong => StrongMarkup::fromArray($data), MarkupType::Emphasized => EmphasizedMarkup::fromArray($data), MarkupType::Monospaced => MonospacedMarkup::fromArray($data), MarkupType::Strikethrough => StrikethroughMarkup::fromArray($data), MarkupType::Underline => UnderlineMarkup::fromArray($data), MarkupType::Heading => HeadingMarkup::fromArray($data), MarkupType::Highlighted => HighlightedMarkup::fromArray($data), MarkupType::Link => LinkMarkup::fromArray($data), MarkupType::UserMention => UserMentionMarkup::fromArray($data), default => throw new LogicException( 'Unknown or unsupported markup type: ' . ($data['type'] ?? 'none') ), }; } }